Chaeyeon0 / GreenDay_Study

여은개의 공부 일지
0 stars 0 forks source link

[20240528] 소셜로그인 리액트 연동 테스트 중 오류 #33

Open janghw0126 opened 1 month ago

janghw0126 commented 1 month ago

본격적으로 아란이와 소셜로그인 연동을 하기 전에 로컬에서 실행이 잘되는지 확인하기 위해서 테스트용으로 리액트를 깔고, 임의로 코드를 작성하였다.

일단 우리는 연동이 시급하므로, 백엔드와 리액트가 연동이 되었을 때 서로 잘 돌아가는지 확인하는 것이 우선이기 때문에 로컬로 먼저 연동테스트를 완료한 후 마지막으로 리액트까지 배포하는 걸로 하였다. (아마두..?)

암튼 임의로 리액트 코드를 작성하고 리액트 연동을 하려는 순간, 잘못된 걸 직감했다. 내가 컨트롤러를 json으로 반환하는 방식이 아니라 템플릿을 반환하는 방식으로 썼다는 것이다,,, 그래서 RESTful 방식으로 후다닥 컨트롤러와 서비스 패키지를 고치고 다시 실행하였다. 그랬더니

localhost:3000에 접속하는 것 부터 문제가 생겼다.

image image

찾아보니 cors 설정을 제대로 못해줘서 생긴 문제였다. 그러니까 애초에 config 패키지에 백엔드와 프론트가 서로의 주소에 접근할 수 있도록 설정을 해놓지 않아서 이런 오류가 생긴 것이였다. 그래서

package practice.login.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import practice.login.domain.MemberRole;

import java.util.List;

// 네이버 소셜로그인을 통합하기 위한 spring security 설정 클래스
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    // SecurityFilterChain 빈을 정의하여 보안 설정을 구성함
    // 시큐리티 필터 메서드
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        // ========= oauth login =========== //
        //접근 권한 설정
        // CORS 설정
        http.cors(c -> {
                    CorsConfiguration config = new CorsConfiguration();
                    config.setAllowedOrigins(List.of("http://localhost:3000")); // 클라이언트 출처를 허용
                    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 사용 가능한 메서드 지정
                    config.setAllowedHeaders(List.of("*")); // 모든 헤더 허용
                    config.setAllowCredentials(true); // 자격 증명을 허용
                    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                    source.registerCorsConfiguration("/**", config);
                    c.configurationSource(source);
                })

                // HTTP 요청에 대한 접근 제어를 구성함
                // HTTP 보안 설정
                .authorizeHttpRequests((auth) -> auth
                        // "/oauth-login/admin" 엔드포인트에 대한 접근은 ADMIN 역할을 가진 사용자만 가능함
                        .requestMatchers("/oauth-login/admin").hasRole(MemberRole.ADMIN.name())
                        // "/oauth-login/info" 엔드포인트에 대한 접근은 인증된 사용자만 가능합니다.
                        .requestMatchers("/oauth-login/info").authenticated()
                        // 다른 모든 요청은 인증 없이 허용됨
                        .anyRequest().permitAll()
                )
                // OAuth2 로그인 설정을 구성함
                .oauth2Login((auth) -> auth.loginPage("/greenday/login")

                        // 로그인 성공 후 리다이렉트할 URL을 localhost:3000/greenday로 변경
                        .defaultSuccessUrl("http://localhost:3000/greenday")
                        // 로그인 실패 후 이동할 URL을 지정함
                        .failureUrl("/greenday/login")
                        // 로그인 페이지에 대한 접근은 인증 없이 허용됨
                        .permitAll()
                )
        // OAuth 2.0 로그인 방식 설정
        // 로그아웃 설정을 구성함
                .logout((auth) -> auth
                        // 로그아웃 URL을 지정함
                        .logoutUrl("/oauth-login/logout")
                )
                // CSRF 보호를 비활성화함
                .csrf((auth) -> auth.disable());
        // 구성된 SecurityFilterChain을 빌드하고 반환함
        return http.build();
    }
    // BCryptPasswordEncoder 빈을 정의하여 비밀번호를 암호화함
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){

        // BCryptPasswordEncoder의 새 인스턴스를 반환함
        return new BCryptPasswordEncoder();
    }
}

cors 설정을 수정하였다. 근데 패키지에 작성한 url도 나중에 프론트가 배포된 이후에 다시 수정해야 할 것 같다. 암튼 cors 설정을 해주고 리액트와 스프링을 실행시켰더니

image image image

다행이도 정상적으로 실행된 것을 확인할 수 있었고 DB에도 잘 담겼다는 것을 확인할 수 있었다.

// Main.js

import React, { useState } from 'react';
import SocialLoginButton from './SocialLoginButton';

const Main = ({ isLoggedIn, onSocialLogin }) => {
  const handleSocialLogin = () => {
    // 소셜 로그인 버튼이 클릭되었을 때 처리할 로직
    // 예를 들어, 네이버 소셜 로그인 페이지로 이동하도록 할 수 있습니다.
    window.location.href = 'http://localhost:3000/oauth2/authorization/naver';
  };

  return (
    <div>
      <h1>메인 화면</h1>
      {!isLoggedIn && <SocialLoginButton onClick={handleSocialLogin} />}
      {isLoggedIn && <p>이미 로그인되었습니다.</p>}
    </div>
  );
};

export default Main;
import React, { useEffect } from 'react';
import axios from 'axios';

const LoginSuccess = ({ setIsLoggedIn, setUser }) => {
    useEffect(() => {
        axios.post('http://localhost:8080/greenday/oauth-success', {}, { withCredentials: true })
            .then(response => {
                setIsLoggedIn(true);
                setUser(response.data);
                window.location.href = '/greenday'; // 로그인 성공 후 페이지 이동
            })
            .catch(error => {
                console.error('로그인 실패:', error);
            });
    }, [setIsLoggedIn, setUser]);

    return (
        <div>
            <h1>로그인 성공!</h1>
        </div>
    );
};

export default LoginSuccess;

이건 리액트 코드인데 아직 찾아보고 있지만 axios에 백엔드 주소를 넣는 것 같다...? 일단 리액트 코드는 챗지피티가 일케 짜도록 도와줬는데 나중에 아란이랑 연동하면서 내 코드를 수정해나가야 될 것 같다.