[게시판 프로젝트] 소셜 로그인 기능 구현(3) - 카카오로 로그인 하기
2024. 8. 27. 03:34ㆍCS/프로젝트
회원 정보 서비스 로직 구현 - UserAccountService
searchUser - 값이 있을 때와 없을 때의 호출자에게 위임하기 위해서 Optional <UserAccountDto> 사용
@Transactional(readOnly = true)
public Optional<UserAccountDto> searchUser(String username) {
return userAccountRepository.findById(username)
.map(UserAccountDto::from);
}
- @Transactional(readOnly = true)
- 이 메서드는 읽기 전용 트랜잭션으로 실행됩니다.
- 데이터베이스에 변경을 가하지 않으므로 성능 최적화가 가능하며, 기본적으로 데이터베이스의 플러시 작업이 발생하지 않습니다.
- Optional<UserAccountDto> searchUser(String username)
- 사용자 이름(username)을 기반으로 사용자를 검색하는 메서드입니다.
- 반환 타입은 Optional<UserAccountDto>입니다. 이는 검색 결과가 없을 경우 Optional.empty()를 반환하여, null 대신 안전한 방법으로 결과를 처리할 수 있게 합니다.
- userAccountRepository.findById(username)
- username으로 사용자 정보를 검색합니다.
- findById는 Optional<UserAccount>를 반환합니다.
- .map(UserAccountDto::from)
- 검색된 UserAccount 객체를 UserAccountDto로 변환합니다.
- UserAccountDto::from은 정적 메서드로, UserAccount 객체를 받아 UserAccountDto로 변환합니다.
saveUser
public UserAccountDto saveUser(String username, String password, String email, String nickname, String memo) {
return UserAccountDto.from(
userAccountRepository.save(UserAccount.of(username, password, email, nickname, memo, username))
);
}
- UserAccountDto saveUser(...)
- 새로운 사용자를 데이터베이스에 저장하는 메서드입니다. 저장된 사용자의 정보를 UserAccountDto로 반환합니다.
- UserAccount.of(...)
- 정적 팩토리 메서드 of를 호출하여 UserAccount 객체를 생성합니다.
- 이 메서드는 사용자 계정 정보를 받아 UserAccount 객체를 생성합니다.
- username: 사용자의 이름입니다. 여기서는 기본 키로 사용됩니다.
- password: 암호화된 비밀번호입니다.
- email: 사용자의 이메일 주소입니다.
- nickname: 사용자의 닉네임입니다.
- memo: 사용자에 대한 메모입니다.
- 마지막 username 인자는 계정의 생성자(생성자 정보를 남길 때)로 사용될 수도 있습니다.
- userAccountRepository.save(...)
- UserAccountRepository의 save 메서드를 호출하여 UserAccount 객체를 데이터베이스에 저장합니다.
- 저장된 객체는 영속화된 상태로 반환됩니다.
- UserAccountDto.from(...)
- 저장된 UserAccount 객체를 UserAccountDto로 변환합니다. 반환 값은 UserAccountDto입니다.
UserAccountServiceTest 로직 구현
searchUser - 존재하는 회원 ID를 검색하면, 회원 데이터를 Optional로 반환
searchUser - 존재하지 않는 회원 ID를 검색하면, 비어있는 Optional을 반환
saveUser - 회원 정보를 입력하면, 새로운 회원 정보를 저장하여 가입시키고 해당 회원 데이터를 반환
- [명확한 검사 의도]
- UserAccountDto result = sut.saveUser 기능을 사용하면
- id, password, email, nickname, memo만 넣어줬는데 -> 검사식에는 createdBy와 modifiedAt이 포함되어있음
- createdBy와 modifiedAt에서 getUserId()를 활용함
인증 설정 업데이트 : OAuth 인증 설정 추가 - OAuth2.0 보안 설정 - SecurityConfig
filterChain 수정
- Before
- After
- SecurityFilterChain: Spring Security의 보안 필터 체인을 정의하는 빈입니다.
- HttpSecurity: 이 객체는 웹 보안을 설정하는 데 사용됩니다.
- OAuth2UserService<OAuth2UserRequest, OAuth2User>: OAuth2 로그인 과정에서 사용자 정보를 처리하는 서비스입니다.
- authorizeHttpRequests: 요청에 대한 권한 설정을 정의합니다.
- 정적 리소스(CSS, JS, 이미지 등)에 대한 요청은 모두 허용됩니다(permitAll()).
- GET 요청 중 "/"(홈), "/articles", "/articles/search-hashtag" 경로에 대해서는 인증이 필요 없습니다.
- 그 외의 모든 요청에 대해서는 인증이 요구됩니다(authenticated()).
- formLogin(withDefaults()): 기본 로그인 페이지를 사용하도록 설정합니다.
- logout: 로그아웃이 성공하면 "/" 경로로 리다이렉트됩니다.
- oauth2Login: OAuth2 로그인 설정입니다.
- userInfoEndpoint: 사용자 정보를 가져오는 엔드포인트 설정에서 OAuth2UserService를 사용해 사용자 정보를 처리합니다.
UserDatailsService 수정
- Before
- After
- UserDetailsService는 스프링 시큐리티에서 사용자의 정보를 가져오는 서비스 인터페이스입니다.
- userAccountService를 사용해 사용자를 검색하고, 결과를 BoardPrincipal 객체로 변환합니다.
- 사용자를 찾지 못하면 UsernameNotFoundException을 발생시킵니다.
OAuthUserService 메서드 추가
- 이 코드는 Spring Security의 OAuth2 인증 과정에서 사용자 정보를 처리하기 위해 사용자 정의 OAuth2UserService를 생성하는 메서드입니다.
- 이 메서드는 사용자가 OAuth2 인증을 통해 로그인할 때 호출되며, 사용자의 정보를 로드하고 애플리케이션의 사용자로 매핑하는 역할을 합니다.
이 코드 블록은 Spring Security에서 OAuth2 인증을 처리하는 로직을 정의합니다.
카카오를 통한 OAuth2 인증 시, 사용자의 정보를 받아 애플리케이션 내에서 적절한 형태로 변환하고 저장하며, 저장된 사용자를 반환하는 과정을 구현하고 있습니다.
이 과정에서 기본적으로 제공되는 DefaultOAuth2UserService를 사용하여 사용자의 정보를 로드하고, 그 정보를 기반으로 사용자 계정의 생성 또는 검색을 처리합니다.
OAuthUserService 메서드 코드 분석
@Bean
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService(
UserAccountService userAccountService,
PasswordEncoder passwordEncoder
) {
- 이 메서드는 OAuth2UserService<OAuth2UserRequest, OAuth2User> 타입의 빈을 생성합니다.
- @Bean 어노테이션을 통해 이 메서드는 Spring 컨텍스트에 등록됩니다.
- userAccountService는 사용자 계정을 관리하는 서비스입니다.
- passwordEncoder는 비밀번호를 암호화하는 도구입니다.
DefaultOAuthUserService 객체 생성
final DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
- DefaultOAuth2UserService는 기본적으로 OAuth2 인증 과정에서 사용자 정보를 가져오는 데 사용되는 서비스입니다.
- delegate라는 변수로 이 기본 서비스를 생성합니다. 이 서비스는 이후에 사용됩니다.
람다 표현식으로 OAuthUserService 구현
return userRequest -> {
- 이 람다 표현식은 OAuth2UserService<OAuth2UserRequest, OAuth2User> 인터페이스를 구현합니다. userRequest라는 매개변수를 받아 처리합니다.
- userRequest는 OAuth2UserRequest 객체로, 사용자가 로그인할 때 OAuth2 공급자로부터 반환된 정보가 담겨 있습니다.
사용자 정보 로드
OAuth2User oAuth2User = delegate.loadUser(userRequest);
- delegate.loadUser(userRequest)를 통해 OAuth2 공급자로부터 사용자의 정보를 로드합니다.
- 이 정보는 OAuth2User 객체에 저장됩니다.
KakaoOAuthResponse 객체 생성
KakaoOAuth2Response kakaoResponse = KakaoOAuth2Response.from(oAuth2User.getAttributes());
- oAuth2User.getAttributes()는 사용자의 속성을 Map<String, Object> 형태로 반환합니다.
- KakaoOAuth2Response.from() 메서드를 사용해 이 속성을 KakaoOAuth2Response 객체로 변환합니다.
- 이 변환 과정은 카카오의 OAuth2 응답을 처리하기 위한 것입니다.
사용자 정보 생성
String registrationId = userRequest.getClientRegistration().getRegistrationId();
String providerId = String.valueOf(kakaoResponse.id());
String username = registrationId + "_" + providerId;
- registrationId: 어떤 OAuth2 공급자를 통해 인증했는지를 나타냅니다 (예: kakao).
- providerId: 사용자의 고유 ID입니다. 카카오에서는 이 ID가 kakaoResponse.id()로부터 추출됩니다.
- username: registrationId와 providerId를 결합하여 고유한 사용자 이름을 생성합니다. 이는 애플리케이션 내에서 사용자를 식별하는 데 사용됩니다.
임시 비밀번호 생성
String dummyPassword = passwordEncoder.encode("{bcrypt}" + UUID.randomUUID());
- dummyPassword: 임시 비밀번호를 생성합니다. 이 비밀번호는 bcrypt 방식으로 암호화되며, 고유한 UUID로 생성된 값이 사용됩니다.
사용자 검색 및 생성
return userAccountService.searchUser(username)
.map(BoardPrincipal::from)
.orElseGet(() ->
BoardPrincipal.from(
userAccountService.saveUser(
username,
dummyPassword,
kakaoResponse.email(),
kakaoResponse.nickname(),
null
)
)
);
- userAccountService.searchUser(username): 데이터베이스에서 사용자를 검색합니다. username은 이전에 생성된 사용자 이름입니다.
- .map(BoardPrincipal::from): 사용자가 존재하면, 이를 BoardPrincipal 객체로 변환하여 반환합니다.
- .orElseGet(() -> ...): 사용자가 존재하지 않으면, 새로운 사용자를 생성합니다.
- userAccountService.saveUser(...)를 통해 새로운 사용자를 데이터베이스에 저장합니다.
- 저장된 사용자를 BoardPrincipal 객체로 변환하여 반환합니다.
최종 반환
};
- 이 람다 표현식은 최종적으로 OAuth2UserService를 구현하는 익명 함수로서 반환됩니다. 이 구현체는 OAuth2 로그인 과정에서 사용자 정보를 로드하고, 해당 사용자가 시스템에 이미 존재하는지 확인한 후, 사용자 정보를 반환하는 역할을 합니다.
'CS > 프로젝트' 카테고리의 다른 글
클라우드 배포하기(1) - AWS란 무엇인가/EC2 생성/Elastic IP 설정 (5) | 2024.09.04 |
---|---|
[게시판 프로젝트] 깃헙 릴리즈하기 (0) | 2024.09.04 |
[게시판 프로젝트] 소셜로그인 기능 구현(2) - 카카오로 로그인 하기 (0) | 2024.08.27 |
[게시판 프로젝트] 소셜로그인 기능 구현(1) - 카카오로 로그인 하기 - 카카오 API 사용준비/의존성/프로퍼티 설정 (0) | 2024.08.27 |
[게시판 프로젝트] 게시글 댓글 구현 - ArticleCommentController 코드 뜯어보며, 댓글 기능 프로세스 이해하기 (0) | 2024.08.26 |