Skip to content

Commit 1e230e4

Browse files
authored
refactor: OAuth 클래스에 전략 패턴 적용 (#432)
* refactor: 전략패턴 적용 * refactor: 컨트롤러에 변경 내용 적용 * chore: 사용되지 않는 코드 제거 * test: 테스트 코드 작성
1 parent 5a2ad88 commit 1e230e4

10 files changed

Lines changed: 225 additions & 91 deletions

File tree

src/main/java/com/example/solidconnection/auth/client/AppleOAuthClient.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
import com.example.solidconnection.auth.client.config.AppleOAuthClientProperties;
77
import com.example.solidconnection.auth.dto.oauth.AppleTokenDto;
88
import com.example.solidconnection.auth.dto.oauth.AppleUserInfoDto;
9+
import com.example.solidconnection.auth.dto.oauth.OAuthUserInfoDto;
10+
import com.example.solidconnection.auth.service.oauth.OAuthClient;
911
import com.example.solidconnection.common.exception.CustomException;
12+
import com.example.solidconnection.siteuser.domain.AuthType;
1013
import io.jsonwebtoken.Jwts;
1114
import java.security.PublicKey;
1215
import java.util.Objects;
@@ -27,20 +30,26 @@
2730
* */
2831
@Component
2932
@RequiredArgsConstructor
30-
public class AppleOAuthClient {
33+
public class AppleOAuthClient implements OAuthClient {
3134

3235
private final RestTemplate restTemplate;
3336
private final AppleOAuthClientProperties properties;
3437
private final AppleOAuthClientSecretProvider clientSecretProvider;
3538
private final ApplePublicKeyProvider publicKeyProvider;
3639

37-
public AppleUserInfoDto processOAuth(String code) {
40+
@Override
41+
public AuthType getAuthType() {
42+
return AuthType.APPLE;
43+
}
44+
45+
@Override
46+
public OAuthUserInfoDto getUserInfo(String code) {
3847
String idToken = requestIdToken(code);
3948
PublicKey applePublicKey = publicKeyProvider.getApplePublicKey(idToken);
4049
return new AppleUserInfoDto(parseEmailFromToken(applePublicKey, idToken));
4150
}
4251

43-
public String requestIdToken(String code) {
52+
private String requestIdToken(String code) {
4453
HttpHeaders headers = new HttpHeaders();
4554
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
4655
MultiValueMap<String, String> formData = buildFormData(code);

src/main/java/com/example/solidconnection/auth/client/KakaoOAuthClient.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
import com.example.solidconnection.auth.client.config.KakaoOAuthClientProperties;
88
import com.example.solidconnection.auth.dto.oauth.KakaoTokenDto;
99
import com.example.solidconnection.auth.dto.oauth.KakaoUserInfoDto;
10+
import com.example.solidconnection.auth.dto.oauth.OAuthUserInfoDto;
11+
import com.example.solidconnection.auth.service.oauth.OAuthClient;
1012
import com.example.solidconnection.common.exception.CustomException;
13+
import com.example.solidconnection.siteuser.domain.AuthType;
1114
import java.util.Objects;
1215
import lombok.RequiredArgsConstructor;
1316
import org.springframework.http.HttpEntity;
@@ -27,12 +30,18 @@
2730
* */
2831
@Component
2932
@RequiredArgsConstructor
30-
public class KakaoOAuthClient {
33+
public class KakaoOAuthClient implements OAuthClient {
3134

3235
private final RestTemplate restTemplate;
3336
private final KakaoOAuthClientProperties kakaoOAuthClientProperties;
3437

35-
public KakaoUserInfoDto getUserInfo(String code) {
38+
@Override
39+
public AuthType getAuthType() {
40+
return AuthType.KAKAO;
41+
}
42+
43+
@Override
44+
public OAuthUserInfoDto getUserInfo(String code) {
3645
String kakaoAccessToken = getKakaoAccessToken(code);
3746
return getKakaoUserInfo(kakaoAccessToken);
3847
}

src/main/java/com/example/solidconnection/auth/controller/AuthController.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
import com.example.solidconnection.auth.service.EmailSignInService;
1616
import com.example.solidconnection.auth.service.EmailSignUpService;
1717
import com.example.solidconnection.auth.service.EmailSignUpTokenProvider;
18-
import com.example.solidconnection.auth.service.oauth.AppleOAuthService;
19-
import com.example.solidconnection.auth.service.oauth.KakaoOAuthService;
18+
import com.example.solidconnection.auth.service.oauth.OAuthService;
2019
import com.example.solidconnection.auth.service.oauth.OAuthSignUpService;
2120
import com.example.solidconnection.common.exception.CustomException;
2221
import com.example.solidconnection.common.exception.ErrorCode;
@@ -40,8 +39,7 @@ public class AuthController {
4039

4140
private final AuthService authService;
4241
private final OAuthSignUpService oAuthSignUpService;
43-
private final AppleOAuthService appleOAuthService;
44-
private final KakaoOAuthService kakaoOAuthService;
42+
private final OAuthService oAuthService;
4543
private final EmailSignInService emailSignInService;
4644
private final EmailSignUpService emailSignUpService;
4745
private final EmailSignUpTokenProvider emailSignUpTokenProvider;
@@ -53,7 +51,7 @@ public ResponseEntity<OAuthResponse> processAppleOAuth(
5351
@Valid @RequestBody OAuthCodeRequest oAuthCodeRequest,
5452
HttpServletResponse httpServletResponse
5553
) {
56-
OAuthResponse oAuthResponse = appleOAuthService.processOAuth(oAuthCodeRequest);
54+
OAuthResponse oAuthResponse = oAuthService.processOAuth(AuthType.APPLE, oAuthCodeRequest);
5755
if (oAuthResponse instanceof OAuthSignInResponse signInResponse) {
5856
refreshTokenCookieManager.setCookie(httpServletResponse, signInResponse.refreshToken());
5957
}
@@ -65,7 +63,7 @@ public ResponseEntity<OAuthResponse> processKakaoOAuth(
6563
@Valid @RequestBody OAuthCodeRequest oAuthCodeRequest,
6664
HttpServletResponse httpServletResponse
6765
) {
68-
OAuthResponse oAuthResponse = kakaoOAuthService.processOAuth(oAuthCodeRequest);
66+
OAuthResponse oAuthResponse = oAuthService.processOAuth(AuthType.KAKAO, oAuthCodeRequest);
6967
if (oAuthResponse instanceof OAuthSignInResponse signInResponse) {
7068
refreshTokenCookieManager.setCookie(httpServletResponse, signInResponse.refreshToken());
7169
}

src/main/java/com/example/solidconnection/auth/service/oauth/AppleOAuthService.java

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/main/java/com/example/solidconnection/auth/service/oauth/KakaoOAuthService.java

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.example.solidconnection.auth.service.oauth;
2+
3+
import com.example.solidconnection.auth.dto.oauth.OAuthUserInfoDto;
4+
import com.example.solidconnection.siteuser.domain.AuthType;
5+
6+
public interface OAuthClient {
7+
8+
OAuthUserInfoDto getUserInfo(String code);
9+
10+
AuthType getAuthType();
11+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.example.solidconnection.auth.service.oauth;
2+
3+
import com.example.solidconnection.common.exception.CustomException;
4+
import com.example.solidconnection.common.exception.ErrorCode;
5+
import com.example.solidconnection.siteuser.domain.AuthType;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.function.Function;
9+
import java.util.stream.Collectors;
10+
import org.springframework.stereotype.Component;
11+
12+
@Component
13+
public class OAuthClientMap {
14+
15+
private final Map<AuthType, OAuthClient> oauthClientMap;
16+
17+
public OAuthClientMap(List<OAuthClient> oAuthClientList) {
18+
this.oauthClientMap = oAuthClientList.stream()
19+
.collect(Collectors.toMap(OAuthClient::getAuthType, Function.identity()));
20+
}
21+
22+
public OAuthClient getOAuthClient(AuthType authType) {
23+
OAuthClient oauthClient = oauthClientMap.get(authType);
24+
if (oauthClient == null) {
25+
throw new CustomException(
26+
ErrorCode.NOT_DEFINED_ERROR,
27+
"처리할 수 있는 OAuthClient가 없습니다. authType: " + authType.name()
28+
);
29+
}
30+
return oauthClient;
31+
}
32+
}
Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.example.solidconnection.auth.service.oauth;
22

3-
43
import com.example.solidconnection.auth.dto.SignInResponse;
54
import com.example.solidconnection.auth.dto.oauth.OAuthCodeRequest;
65
import com.example.solidconnection.auth.dto.oauth.OAuthResponse;
@@ -12,50 +11,45 @@
1211
import com.example.solidconnection.siteuser.domain.SiteUser;
1312
import com.example.solidconnection.siteuser.repository.SiteUserRepository;
1413
import java.util.Optional;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.stereotype.Service;
1516
import org.springframework.transaction.annotation.Transactional;
1617

1718
/*
1819
* OAuth 제공자로부터 이메일을 받아 기존 회원인지, 신규 회원인지 판별하고, 이에 따라 다르게 응답한다.
1920
* 기존 회원 : 로그인한다.
2021
* 신규 회원 : 회원가입할 때 필요한 정보를 제공한다.
2122
* */
22-
public abstract class OAuthService {
23+
@Service
24+
@RequiredArgsConstructor
25+
public class OAuthService {
2326

2427
private final OAuthSignUpTokenProvider OAuthSignUpTokenProvider;
2528
private final SignInService signInService;
2629
private final SiteUserRepository siteUserRepository;
27-
28-
protected OAuthService(OAuthSignUpTokenProvider OAuthSignUpTokenProvider, SiteUserRepository siteUserRepository, SignInService signInService) {
29-
this.OAuthSignUpTokenProvider = OAuthSignUpTokenProvider;
30-
this.siteUserRepository = siteUserRepository;
31-
this.signInService = signInService;
32-
}
30+
private final OAuthClientMap oauthClientMap;
3331

3432
@Transactional
35-
public OAuthResponse processOAuth(OAuthCodeRequest oauthCodeRequest) {
36-
OAuthUserInfoDto userInfoDto = getOAuthUserInfo(oauthCodeRequest.code());
37-
String email = userInfoDto.getEmail();
38-
Optional<SiteUser> optionalSiteUser = siteUserRepository.findByEmailAndAuthType(email, getAuthType());
33+
public OAuthResponse processOAuth(AuthType authType, OAuthCodeRequest codeRequest) {
34+
OAuthClient oauthClient = oauthClientMap.getOAuthClient(authType);
35+
OAuthUserInfoDto userInfo = oauthClient.getUserInfo(codeRequest.code());
36+
Optional<SiteUser> optionalSiteUser = siteUserRepository.findByEmailAndAuthType(userInfo.getEmail(), authType);
3937

4038
if (optionalSiteUser.isPresent()) {
4139
SiteUser siteUser = optionalSiteUser.get();
4240
return getSignInResponse(siteUser);
4341
}
4442

45-
return getSignUpPrepareResponse(userInfoDto);
43+
return getSignUpPrepareResponse(userInfo, authType);
4644
}
4745

48-
protected final OAuthSignInResponse getSignInResponse(SiteUser siteUser) {
46+
private OAuthSignInResponse getSignInResponse(SiteUser siteUser) {
4947
SignInResponse signInResponse = signInService.signIn(siteUser);
5048
return new OAuthSignInResponse(true, signInResponse.accessToken(), signInResponse.refreshToken());
5149
}
5250

53-
protected final SignUpPrepareResponse getSignUpPrepareResponse(OAuthUserInfoDto userInfoDto) {
54-
String signUpToken = OAuthSignUpTokenProvider.generateAndSaveSignUpToken(userInfoDto.getEmail(), getAuthType());
51+
private SignUpPrepareResponse getSignUpPrepareResponse(OAuthUserInfoDto userInfoDto, AuthType authType) {
52+
String signUpToken = OAuthSignUpTokenProvider.generateAndSaveSignUpToken(userInfoDto.getEmail(), authType);
5553
return SignUpPrepareResponse.of(userInfoDto, signUpToken);
5654
}
57-
58-
protected abstract OAuthUserInfoDto getOAuthUserInfo(String code);
59-
60-
protected abstract AuthType getAuthType();
6155
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.example.solidconnection.auth.service.oauth;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThatCode;
5+
import static org.junit.jupiter.api.Assertions.assertAll;
6+
import static org.mockito.BDDMockito.given;
7+
import static org.mockito.Mockito.mock;
8+
9+
import com.example.solidconnection.common.exception.CustomException;
10+
import com.example.solidconnection.common.exception.ErrorCode;
11+
import com.example.solidconnection.siteuser.domain.AuthType;
12+
import java.util.List;
13+
import org.junit.jupiter.api.DisplayName;
14+
import org.junit.jupiter.api.Test;
15+
16+
@DisplayName("OAuthClientMap 테스트")
17+
class OAuthClientMapTest {
18+
19+
@Test
20+
void AuthType에_해당하는_Client를_반환한다() {
21+
// given
22+
OAuthClient appleClient = mock(OAuthClient.class);
23+
OAuthClient kakaoClient = mock(OAuthClient.class);
24+
given(appleClient.getAuthType()).willReturn(AuthType.APPLE);
25+
given(kakaoClient.getAuthType()).willReturn(AuthType.KAKAO);
26+
27+
OAuthClientMap oAuthClientMap = new OAuthClientMap(
28+
List.of(appleClient, kakaoClient)
29+
);
30+
31+
// when & then
32+
assertAll(
33+
() -> assertThat(oAuthClientMap.getOAuthClient(AuthType.APPLE)).isEqualTo(appleClient),
34+
() -> assertThat(oAuthClientMap.getOAuthClient(AuthType.KAKAO)).isEqualTo(kakaoClient)
35+
);
36+
}
37+
38+
@Test
39+
void AuthType에_매칭되는_Client가_없으면_예외가_발생한다() {
40+
// given
41+
OAuthClient appleClient = mock(OAuthClient.class);
42+
given(appleClient.getAuthType()).willReturn(AuthType.APPLE);
43+
44+
OAuthClientMap oAuthClientMap = new OAuthClientMap(
45+
List.of(appleClient)
46+
);
47+
48+
// when & then
49+
assertThatCode(() -> oAuthClientMap.getOAuthClient(AuthType.KAKAO))
50+
.isInstanceOf(CustomException.class)
51+
.hasMessageContaining(ErrorCode.NOT_DEFINED_ERROR.getMessage());
52+
}
53+
}

0 commit comments

Comments
 (0)