๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป ํ”„๋กœ์ ํŠธ/๐Ÿ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๐Ÿ 

[SpringSecurity] Vue์™€ Spring์˜ ์„œ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ ๋ฌธ์ œ ํ•ด๊ฒฐํ•˜๊ธฐ.(CORS ์—๋Ÿฌ ํ•ด๊ฒฐํ•˜๊ธฐ)

by ._.sori 2025. 8. 30.

 

 

 

vue์™€ spring์€
์„œ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์„ ๊ฐ€์ ธ ์ƒ๊ธฐ๋Š”
CORS ์—๋Ÿฌ๋ฅผ ํ•ด๊ฒฐํ•ด๋ณด์ž.

 

 

 

 

 

1. ๋ฌธ์ œ ์‚ดํ”ผ๊ธฐ

 

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๋Š” Vue๋กœ ํ”„๋ก ํŠธ๋ฅผ ๋งก๊ณ  ์žˆ๊ณ , Spring์œผ๋กœ ๋ฐฑ์—”๋“œ๋ฅผ ๋งก๊ณ  ์žˆ๋‹ค. Vue์—์„œ ํšŒ์›๊ฐ€์ž…์— ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•œ ํ›„ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋„ Spring์—” ์–ด๋–ค ๋ฐ์ดํ„ฐ๋„ ๋„์ฐฉํ•˜์ง€ ์•Š์•˜๋‹ค. ์™œ ๊ทธ๋Ÿฐ๊ฑธ๊นŒ?

 

Vue๋Š” http://localhost:5173/๋ฅผ ๊ธฐ๋ณธ ์ฃผ์†Œ๋กœ ์‚ฌ์šฉํ•˜๊ณ  Spring์€ http://localhost:8080/์„ ๊ธฐ๋ณธ ์ฃผ์†Œ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. Spring์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ ์˜จ ์š”์ฒญ์„ ๊ฑฐ๋ถ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, Vue์—์„œ ์˜จ ์š”์ฒญ์ด ์ œ๋Œ€๋กœ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด Spring ๋‚ด๋ถ€์— ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์•ผํ•œ๋‹ค.

 

 

 

 


 

 

 

 

 

2. ์ฝ”๋“œ ์‚ดํ”ผ๊ธฐ

  • @CrossOrigin + http.cors( ) ์ฝ”๋“œ ์˜ˆ์‹œ
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://localhost:5173")
public class RegisterController {

    @Autowired
    private MemberService memberService;

    @PostMapping("/register")
    public ResponseEntity<String> register(@RequestBody RegisterRequest request) {

        ....
        
    }
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .cors(cors -> {}); 
        return http.build();
    }

}
@CrossOrigin์€ origins์— ์ ์€ ๊ฒฝ๋กœ๋กœ ์š”์ฒญ์ด ์˜ฌ ๊ฒฝ์šฐ ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ž‘๋™ํ•˜๋„๋ก ์—ฐ๊ฒฐํžŒ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ @CrossOrigin๋งŒ ์„ค์ •ํ•œ๋‹ค๊ณ  ํ•ด๊ฒฐ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ €๋Š” ๋ณธ์š”์ฒญ ์ „์— ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ(Preflight)๋ฅผ ๋ณด๋‚ธ๋‹ค. ์ด ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ ์š”์ฒญ์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์‹ค์ œ ์š”์ฒญ ์ „์— ์„œ๋ฒ„์— ๋ณด๋‚ด๋Š” ์‚ฌ์ „ ๊ฒ€์‚ฌ ์š”์ฒญ์ด๋‹ค. ์ด ์š”์ฒญ์„ ํ†ตํ•ด ๋ธŒ๋ผ์šฐ์ €๋Š” ์„œ๋ฒ„๊ฐ€ ํ•ด๋‹น ์š”์ฒญ์„ ํ—ˆ์šฉํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์ด๋•Œ ํ”„๋ฆฌํ”Œ๋ผ์ดํ”„ ์š”์ฒญ์€ OPTIONS ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์„œ๋ฒ„๋กœ ๋ณด๋‚ด์ง€๋ฉฐ, ์„œ๋ฒ„๋Š” ์ด ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ CORS ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‹ค์ œ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ๋ ค์ค€๋‹ค. 

ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ๋Š” ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ๋ฅผ ๋จผ์ € ์ง€๋‚˜๊ฐ€๋ฉด์„œ http.cors( )๊ฐ€ ๊บผ์ ธ ์žˆ์œผ๋ฉด, ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•ด 401 ํ˜น์€ 403์ด ๋‚˜๊ณ  @CrossOrigin์ด ์žˆ๋Š” ๊ณณ๊นŒ์ง€ ๋„๋‹ฌํ•˜์ง€ ๋ชปํ•œ๋‹ค.
๊ทธ๋ž˜์„œ SpringSecurity ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์–ด๋…ธํ…Œ์ด์…˜ ์ฃผ์ž…ํ•˜๊ณ  SecurityFilterChain์—์„œ http.cors(cors -> {});๋ฅผ ์ ์–ด ํ†ต๊ณผ๋˜๊ฒŒ ํ•ด์ค€๋‹ค.

๋ฌผ๋ก  ์ด ๋ฐฉ๋ฒ•๋ง๊ณ  ๋” ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ๋ฐ”๋กœ CORS๋ฅผ ์ „์—ญ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. @CrossOrigin์ด ์ปจํŠธ๋กค๋Ÿฌ๋งˆ๋‹ค ๋ถ™์–ด์žˆ์–ด์„œ ๊ด€๋ฆฌ๋น„์šฉ์ด ์˜ฌ๋ผ๊ฐ€๊ณ  ์ถฉ๋Œ์ด ์ผ์–ด๋‚  ์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Ÿฐ ์ ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ „์—ญ ๊ด€๋ฆฌ๋ฅผ ํ•ด์ฃผ๋Š”๊ฒŒ ๋” ์ข‹๋‹ค. (๊ทผ๋ฐ ๋‚œ ์•ˆํ–ˆ๋‹ค)

 

 

 

  • ์ „์—ญ ์„ค์ • ์ฝ”๋“œ ์˜ˆ์‹œ
@Configuration
public class SecurityConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:5173")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

 

 

๋งคํ•‘์€ ๋ชจ๋“  ๊ฒฝ๋กœ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก /**๋กœ ์„ค์ •ํ–ˆ๊ณ , ๋„๋ฉ”์ธ์€ http://localhost:5173์ด ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด localhost:5173์œผ๋กœ ๋ถ€ํ„ฐ ์˜จ ๋ชจ๋“  ๊ฒฝ๋กœ์ธ http://localhost:5173/login ํ˜น์€ http://localhost:5173/register ๋“ฑ ๋‹ค ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค์ •ํ•œ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„ ์š”์ฒญ ๋ฐฉ์‹์€ "GET", "POST", "PUT", "DELETE"๋งŒ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ–ˆ๋‹ค.

.allowedHeaders("*")๋Š” ๋ชจ๋“  ํ—ค๋”๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ํ—ค๋”๋Š” Host(์š”์ฒญ ์„œ๋ฒ„ ์ฃผ์†Œ), Origin(์š”์ฒญ์„ ๋ณด๋‚ธ ๋„๋ฉ”์ธ), Authorization(JWT ํ† ํฐ ๊ฐ™์€ ์ธ์ฆ ์ •๋ณด), Content-Type(ํ…์ŠคํŠธ ํ˜น์€ Json ๋“ฑ), ์ฟ ํ‚ค์™€ ๊ฐ™์€ HTTP ์š”์ฒญ/์‘๋‹ต์— ๋ถ™๋Š” ๋ถ€๊ฐ€ ์ •๋ณด๋ฅผ ๋งํ•œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด ์š”์ฒญ์— ์ด๋Ÿฐ ํ—ค๋”๋“ค์„ ๋ถ™์—ฌ๋„ ๋˜๋Š”์ง€ ์„œ๋ฒ„์—๊ฒŒ ๋ฌป๋Š”๋ฐ, *์„ ํ†ตํ•ด ๋ชจ๋“  ํ—ค๋”๋ฅผ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค์ •ํ•ด๋‘” ๊ฒƒ์ด๋‹ค. ๋‚ด๊ฐ€ ๋ณด๋‚ผ ์š”์ฒญ์— ํ•„์š”ํ•œ ํ—ค๋”๋“ค์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•ด๋‘ฌ์•ผ CORS ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

.allowCredentials(true)์€ ์ž๊ฒฉ ์ฆ๋ช…์— ๊ด€ํ•œ ๊ฒƒ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ํ•จ๊ป˜ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฏผ๊ฐํ•œ ์ธ์ฆ ์ •๋ณด๋ฅผ ๋งํ•œ๋‹ค. ๋ณดํ†ต ์ž๊ฒฉ์ฆ๋ช…์€ ์ฟ ํ‚ค, Authorization ํ—ค๋” ๋“ฑ์ด ์žˆ๋‹ค. ํ”„๋ก ํŠธ์—์„œ ๋ฐฑ์—”๋“œ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ฟ ํ‚ค๋‚˜ ์ธ์ฆํ—ค๋”๋ฅผ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ž๋™์œผ๋กœ ๋ถ™์ผ ์ˆ˜ ์žˆ๋Š”์ง€ ์„ค์ •ํ•œ๋‹ค. true๋Š” ๋‹น์—ฐํžˆ ํฌํ•จํ•ด๋„ ์ข‹๋‹ค๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. 

 

 

 

- ์ „์—ญ ์„ค์ • ์ฝ”๋“œ ์ถœ์ฒ˜

 

Spring Boot์—์„œ CORS ์„ค์ •ํ•˜๊ธฐ: ํฌ๋กœ์Šค ๋„๋ฉ”์ธ ์š”์ฒญ ํ—ˆ์šฉ์„ ์œ„ํ•œ ํ•„์ˆ˜ ๊ฐ€์ด๋“œ

์›น ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค ๋ณด๋ฉด ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ ํ†ต์‹ ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ์ž์ฃผ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ์ค‘์š”ํ•œ ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜๊ฐ€ **CORS(Cross-Origin Resource Sharing)**์ž…๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” CORS์˜

digitalbourgeois.tistory.com

 

 

 

 


 

 

 

3. ์‹ค์ œ ์ฝ”๋“œ ์˜ˆ์‹œ

//์˜์กด์„ฑ์ฃผ์ž…
@Configuration
//์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  ์›น ๋ณด์•ˆ ์„ค์ •์„ ๊ตฌ์„ฑ
@EnableWebSecurity
public class SecurityConfig {
    //๋นˆ๋“ฑ๋ก
    @Bean
    //์‹ค์ œ ๋ณด์•ˆ ๊ทœ์น™(์ธ๊ฐ€/์ธ์ฆ/CSRF/CORS ๋“ฑ)์„ ์ •์˜ํ•œ ๋นˆ
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                // ์˜ค๋ฅ˜๋กœ ์ธํ•ด ํ—ˆ์šฉํ•ด์คŒ -> ๊ฐœ๋ฐœ ์ดํ›„ ๋‹ค์‹œ ์ˆ˜์ •ํ•ด์•ผํ•จ
                .authorizeHttpRequests(authz -> authz
                        //ํ•ด๋‹น ๊ฒฝ๋กœ๋Š” ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก
                        .requestMatchers("/api/register").permitAll()
                        .requestMatchers("/api/login").permitAll() 
                        //DB๋„ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก
                        .requestMatchers("/h2-console/**").permitAll()
                        //Kakao ๋กœ๊ทธ์ธ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก
                        .requestMatchers("/oauth/kakao").permitAll()
                        // ๋ชจ๋“  ์š”์ฒญ ์ธ์ฆ ์—†์ด ํ—ˆ์šฉ
                        .anyRequest().permitAll() 
                )
                //ํ—ค๋” ์„ค์ •, H2(DB) ์ฝ˜์†”์„ iframe์œผ๋กœ ๋„์šฐ๊ธฐ ์œ„ํ•ด ์„ค์ •
                .headers(headers
                        -> headers.frameOptions(frameOptions -> frameOptions.disable()))
                //CORS ํ—ˆ์šฉํ•˜๊ฒ ๋‹ค๊ณ  ์ •์˜        
                .cors(cors -> {}); 
        //์œ„์˜ ์„ค์ •๊ฐ’์„ ํ† ๋Œ€๋กœ SecurityFilterChain ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋นˆ์œผ๋กœ ๋“ฑ๋ก
        return http.build();
    }

    // ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ ๋นˆ, ํšŒ์›๊ฐ€์ž… ์‹œ ํ•ด์‹œ ์ €์žฅํ•˜๊ณ  ๋กœ๊ทธ์ธ ์‹œ ํ•ด์‹œ ๋น„๊ต์— ํ•„์š”
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

 

 

 

- @EnableWebSercurity์™€ SecurityFilterChain

 

[SpringSecurity] @EnableWebSecurity, SecurityFilterChain ๊ฐœ๋…๊ณผ ์˜ˆ์‹œ

@EnableWebSecurity๋Š”์–ธ์ œ ์“ฐ๋Š”๊ฑธ๊นŒ? @EnableWebSecurity์–ด๋…ธํ…Œ์ด์…˜์„ ๋“ฑ๋กํ•˜์—ฌ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  ์›น ๋ณด์•ˆ ์„ค์ •์„ ๊ตฌ์„ฑํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.์ฆ‰, ๋“ฑ๋กํ•˜๋ฉด ๋ณด์•ˆ๊ณผ ๊ด€๋ จ๋œ ๋นˆ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค

post-this.tistory.com

 

'๐Ÿ’ป ํ”„๋กœ์ ํŠธ > ๐Ÿ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๐Ÿ ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Vue.js] ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ, localStorage๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์•„์ด๋””๊ฐ€ ๋ฉ”์ธํ™”๋ฉด์— ๋ณด์ด๋„๋ก ์„ค์ •ํ•˜๊ธฐ!  (2) 2025.09.02
[SpringBoot, Vue.js] ๋กœ๊ทธ์ธ ํ™”๋ฉด ๋งŒ๋“ค๊ธฐ, passwordEncoder.matches๋ฅผ ํ†ตํ•ด ์•”ํ˜ธํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋น„๊ตํ•˜๊ธฐ.  (5) 2025.08.31
[Vue.js] ๋ฒ„ํŠผ ๋กœ์ง์ด ์ œ๋Œ€๋กœ ๋™์ž‘์„ ์•ˆํ•˜๋Š” ๊ฒƒ ๊ฐ™์„ ๋•Œ, chunk-VZXQDS5F.js?v=5f04997f:2125 [Vue warn]: Data property "idDuplicate" is already defined in Methods.  (0) 2025.08.20
[SpringBoot] (IntelliJ, vue.js, H2) ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ 3ํŽธ: watch๋กœ ์•„์ด๋””์™€ ์ด๋ฉ”์ผ ๋ณ€๊ฒฝ ์‹œ ๋‹ค์‹œ ์ค‘๋ณต์ฒดํฌํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ.  (4) 2025.08.18
[SpringBoot] (IntelliJ, vue.js, H2) ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ 2ํŽธ : ์•„์ด๋””, ์ด๋ฉ”์ผ ์ค‘๋ณต์ฒดํฌ ๋งŒ๋“ค๊ธฐ.  (11) 2025.08.17