멀티모듈의 패키지 구성요소에 따라서 서비스 레이어를 분리해서 서비스 계층의 책임을 재분배하는 리팩토링을 거칠려고한다.
그 전에는 필요한 의존성만 가져와서 써야하는데 계층의 책임이 맞지 않게 가져다 사용해서 여러 문제가 발생했다..
우선 CustomOAuth2UserService 부터 변경해보자.
기존 코드는
package sellyourunhappiness.core.user.application;
import static sellyourunhappiness.core.user.domain.enums.SocialType.*;
import java.util.Collections;
import java.util.Map;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import sellyourunhappiness.core.security.CustomOAuth2User;
import sellyourunhappiness.core.user.domain.User;
import sellyourunhappiness.core.user.domain.enums.SocialType;
import sellyourunhappiness.core.user.infrastructure.UserRepository;
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private final UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
String Accesstoken = userRequest.getAccessToken().getTokenValue();
System.out.println("Accesstoken = " + Accesstoken);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
SocialType socialType = getSocialType(registrationId);
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
OAuthAttributes extractAttributes = OAuthAttributes.of(socialType, userNameAttributeName, oAuth2User.getAttributes());
Map<String, Object> attributes = oAuth2User.getAttributes();
User createdUser = getUser(extractAttributes, socialType);
return new CustomOAuth2User(
Collections.singleton(new SimpleGrantedAuthority(createdUser.getRole().getKey())),
attributes,
extractAttributes.getNameAttributeKey(),
createdUser.getEmail(),
createdUser.getRole()
);
}
private SocialType getSocialType(String registrationId) {
if ("google".equals(registrationId)) {
return GOOGLE;
}
throw new IllegalArgumentException("Unknown SocialType: " + registrationId);
}
private User getUser(OAuthAttributes attributes, SocialType socialType) {
User findUser = userRepository.findBySocialTypeAndEmail(socialType,
attributes.getOauth2UserInfo().getEmail()).orElse(null);
if(findUser == null) {
return saveUser(attributes, socialType);
}
return findUser;
}
private User saveUser(OAuthAttributes attributes, SocialType socialType) {
User createdUser = attributes.toEntity(socialType, attributes.getOauth2UserInfo());
return userRepository.save(createdUser);
}
}
위와 같았는데 해당 코드에서는 loadUser만 사용하므로 UserService와 CustomOAuth2UserService로 분리해보자.
package sellyourunhappiness.global.dto;
import static sellyourunhappiness.core.user.domain.enums.SocialType.*;
import java.util.Collections;
import java.util.Map;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import sellyourunhappiness.api.user.application.UserBroker;
import sellyourunhappiness.core.security.CustomOAuth2User;
import sellyourunhappiness.core.user.application.OAuthAttributes;
import sellyourunhappiness.core.user.domain.User;
import sellyourunhappiness.core.user.domain.enums.SocialType;
@RequiredArgsConstructor
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private final UserBroker userBroker;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
SocialType socialType = getSocialType(registrationId);
String userNameAttributeName = userRequest.getClientRegistration()
.getProviderDetails()
.getUserInfoEndpoint()
.getUserNameAttributeName();
OAuthAttributes extractAttributes = OAuthAttributes.of(socialType, userNameAttributeName,
oAuth2User.getAttributes());
Map<String, Object> attributes = oAuth2User.getAttributes();
User createdUser = userBroker.getUser(extractAttributes, socialType);
return new CustomOAuth2User(
Collections.singleton(new SimpleGrantedAuthority(createdUser.getRole().getKey())),
attributes,
extractAttributes.getNameAttributeKey(),
createdUser.getEmail(),
createdUser.getRole()
);
}
private SocialType getSocialType(String registrationId) {
if ("google".equals(registrationId)) {
return GOOGLE;
}
throw new IllegalArgumentException("SocialType이 일치하지 않습니다.: " + registrationId);
}
}
이건 CustomOAuth2UserService이다. 원래는 하위모듈에서 사용되었지만 로그인과정에서 사용되는 서비스이므로 상위 모듈로 필요한 코드들만 가지고 옮겼다.
package sellyourunhappiness.core.user.application;
import java.util.Optional;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import sellyourunhappiness.core.user.domain.User;
import sellyourunhappiness.core.user.domain.enums.SocialType;
import sellyourunhappiness.core.user.infrastructure.UserRepository;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public Optional<User> findByEmail(String email) {
return userRepository.findByEmail(email);
}
public User saveUser(OAuthAttributes attributes, SocialType socialType) {
User createdUser = attributes.toEntity(socialType, attributes.getOauth2UserInfo());
return userRepository.save(createdUser);
}
public Optional<User> findBySocialTypeAndEmail(SocialType socialType, String email) {
return userRepository.findBySocialTypeAndEmail(socialType, email);
}
}
이건 UserService이다. 기존에는 계층이 안나눠져있어서 다른 코드들의 의존성을 받아 사용했지만 변경 후에는 UserRepository에만 의존하게 수정했다. UserController에서 사용하기전에 UserBroker를 추가해 책임을 더 분리했다.
package sellyourunhappiness.api.user.application;
import java.util.Map;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import sellyourunhappiness.core.user.application.JwtService;
import sellyourunhappiness.core.user.application.OAuthAttributes;
import sellyourunhappiness.core.user.application.UserService;
import sellyourunhappiness.core.user.domain.User;
import sellyourunhappiness.core.user.domain.enums.SocialType;
@RequiredArgsConstructor
@Service
public class UserBroker {
private final UserService userService;
private final JwtService jwtService;
public User getUserByEmail(String email) {
return userService.findByEmail(email)
.orElseThrow(() -> new IllegalArgumentException("유저를 찾을 수 없습니다.: " + email));
}
public Map<String, String> refreshAccessToken(String refreshToken) {
return jwtService.refreshAccessToken(refreshToken);
}
public User getUser(OAuthAttributes attributes, SocialType socialType) {
User findUser = userService.findBySocialTypeAndEmail(socialType, attributes.getOauth2UserInfo().getEmail())
.orElseGet(() -> userService.saveUser(attributes, socialType));
return findUser;
}
}
UserBroker는 UserService만 가져와서 사용하기때문에 각 서비스마다 책임이 분리되어서 사용되는 개념이다.
이렇게 UserService와 관련된 코드들을 분리해서 나눠봤습니다.
'Spring' 카테고리의 다른 글
정적 팩토리 메서드 패턴의 중요성 (0) | 2024.07.17 |
---|---|
Error creating bean with name 'jpaAuditingHandler' 에러 해결 (0) | 2024.02.01 |
ApplicationTests > contextLoads() FAILED 에러 해결 (1) | 2024.02.01 |
메세지 큐를 활용한 트랜잭션 관리 - 기술적 챌린지 (1) | 2023.08.22 |
웹 애플리케이션 이해 (0) | 2023.07.06 |