스프링 부트와 AWS로 혼자 구현하는 웹서비스라는 책을 따라하며 백엔드 서버를 혼자 잘 구현했다.
그러고 프론트엔드로 구현해놓은 리액트와 연결하려 했다. (프론트는 다른 사람이 했음)
역시나~ 잘 될리가..ㅎ
백엔드의 진짜 문제는 서버 구동하면서 같다. 코딩은 백엔드의 10프로 정도만 하지 않나... 싶음...
우선 생긴 문제는 크게
1. CORS 오류
2. 스프링 시큐리티의 로그인 문제
이렇게다.
*
본 포스트는 제가 나름대로 이해하고 생각한 내용입니다. 실제 구동 방식과 다를 수도 있습니다.
실제 정확한 원리와 개념은 다른 포스트를 보세요... 부끄러워요...
*
책에서 나온 코드를 기준으로 작성되었습니다.
책 GitHub 주소: https://github.com/jojoldu/freelec-springboot2-webservice
1. CORS 오류
- 왜 오류가 떴을까?
: 서버는 localhost:8080, 리액트는 localhost:3000으로 접근했다.
CORS 자체가 이렇게 서버랑 클라이언트가 다를 때 생기는 문제가 아닌가. 그래서 클라이언트에서 접근하는 주소를 허용해야 한다.
- 해결 방법
우선 웹과, 스프링 시큐리티 두 곳에서 모두 CORS를 허용해줘야 한다.
로그인 리다이렉트를 보낼 때가 있으므로!
WebConfig.java
@Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**")
.allowedOrigins(
"http://localhost:8080",
"http:{배포한 ec2 ip}:8080",
"http://localhost:3000"
)
.allowedMethods("GET", "POST", "PUT", "DELETE");
}
나는 ec2로 배포해서 내 로컬 호스트, 리액트 로컬, ec2 이렇게 허용했다.
SecurityConfig.java
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
나는 귀찮아서 와일드카드 썼는데 헤더랑 메소드 모두 위처럼 명확히 명시해주는 게 좋다.
여기서는 리액트 로컬만 추가해줬는데 추후에 리액트를 서버에 배포하면 주소 다시 수정해줘야 한다.
추가로 나는 apiController들에
@CrossOrigin("*")
이 어노테이션도 추가해줬는데 위 설정만으로 충분한 것 같다.
만약 api만 배포한다면 이 어노테이션만으로 충분할 것 같으나, 나는 시큐리티를 이용하므로 위 설정도 필요했다.
2. 카카오 로그인 관련 CORS 오류
요건 리액트 문제였다.
혹시 axios를 이용했다면 href로 보내기!
https://devtalk.kakao.com/t/rest-api-cors/114424
Rest API 로 로그인 구현 시 CORS 문제
rest API 로 로그인을 구현 중인데요. 아래와 같이 요청을 보내고 있습니다. `axios.get('https://kauth.kakao.com/oauth/authorize', { params: { client_id: process.env.REACT_APP_REST_API_KEY as string, redirect_uri: 'http://localhost:808
devtalk.kakao.com
3. 로그인 후 알 수 없는 페이지로 이동
이게 무슨 말이냐면... "/"에서 로그인을 시도하면 "/"로 잘 돌아와야 한다.
그런데 자꾸 이상한 알 수 없는 페이지로 들어가서 뭘까 싶었는데
":3000/" -> 로그인 요청 -> ":8080/"로 리다이렉트 이렇게 되는 거였다.,,
이건 스프링 시큐리티에서 자동으로 처리해주는 핸들링이라 스프링이 배포되어 있던 백엔드 서버로 리다렉 되던 거였음.
이런 경우엔 커스텀 핸들러를 따로 만들어야 한다.
SecurityConfig.java
.oauth2Login()
.defaultSuccessUrl("/login/success", true)
.successHandler(loginHandler)
.userInfoEndpoint()// 로그인 성공 이후 사용자 정보 가져올 때
.userService(customOAuth2UserService); //소셜 로그인 성공 후 인터페이스 구현체 등록(ex. sns에서 가져오고 싶은 사용자 정보 기능 명시 가능
이런 식으로 성공 시 돌아갈 url과 핸들러를 매핑해준 다음
loginHandler.java
@Component
public class LoginHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
String url = "http://localhost:3000/";
getRedirectStrategy().sendRedirect(request, response, url);
}
}
이렇게 강제로 핸들링을 해줬다.
이거 코드만 봐도 쎄하다... 이렇게 url을 바로 매핑해주는 게 좀... 걸리다...
모른 척 하려고 했는데 역시 문제가 생겼다.
이렇게 해주면
8080 백엔드 서버에서는 세션이 유지가 되는데, 3000 리액트에서는 세션이 유지가 안 되는 거다.
결국 요청을 3000에서 해야 하는데 서로 맞지 않으니 이상한 거다.
어쩔 수 없이 jwt 토큰으로 통신을 해야 한다...
만약 리액트, 스프링 한 명이 다 해서 같은 주소로 배포하면 이 문제들은 없지 않을까 싶다.
나도 그렇게 할까 했다가 근본적인 문제가 해결이 안 되는 것 같아 그냥 방법을 틀기로 했다.
이건 2편에 이어 쓰도록 하겠습니다.