-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
6주차 : 로그인기능 #82
base: tmxhsk99
Are you sure you want to change the base?
6주차 : 로그인기능 #82
Conversation
- VALID_TOKEN 추가
- 파싱 결과 형 변환 추가
Assertions.assertThat(accessToken).isNotNull(); | ||
Assertions.assertThat(accessToken).isEqualTo(VALID_TOKEN); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래에서 문자열과 비교하고 있으니 null인지는 확인하지 않아도 될 것 같아요. 그리고 null이 반환될 수 있다는 것 자체가 코드를 이해하기 어렵게 만드는데, 아예 null을 반환하지 않도록 하는게 좋아요. 물론 코틀린처럼 null을 허용하는 문법을 제공한다면 좀 다르긴 하지만요.
private final String SECRET = "12345678901234567890123456789010"; | ||
private final String VALID_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjF9.neCsyNLzy3lQ4o2yliotWT06FwSGZagaHpKdAkjnGGw"; | ||
JwtUtil jwtUtil = new JwtUtil(SECRET); | ||
private final Long USER_ID = 1L; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JwtUtil jwtUtil = new JwtUtil(SECRET);
은 상수는 아니라서 구분해 주는 게 좋을 것 같아요'
private final String SECRET = "12345678901234567890123456789010";
private final String VALID_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjF9.neCsyNLzy3lQ4o2yliotWT06FwSGZagaHpKdAkjnGGw";
private final Long USER_ID = 1L;
JwtUtil jwtUtil = new JwtUtil(SECRET);
커밋을 작게 나눌려고 노력하시는 군요. 좋은 커밋을 작성하는 방법 중 하나를 소개해 드리고 싶네요. 커밋은 제목과 본문이 있는데요. 제목은 가장 위에, 본문은 한 줄 띄고 그 다음부터가 본문이죠. 커밋의 제목은 이 커밋을 추가한 이유가 드러나야하고, 본문은 그 이유를 달성하기 위해서 실제로 한 일을 구체적으로 적어주어야 해요.
이 위 일들을 의미를 커밋에 담으려면 지금 하신 것처럼
그래서 정말 이유가 드러나도록 커밋을 작성한다면 커밋은 다음과 같이 나눌 수 있을것 같아요.
본문은 이런식으로 쓰면 좋아요. 대부분 사람들이 다른 사람들도 내가 아는 지식을 알 것이라고 가정하는 실수를 하기 때문에 |
뭔가 커밋 메시지를 작성하면서도 항상 탐탁치 않은 부분이 있었는데 |
AuthenticationService 의 테스트에는 해피테스트만 존재하는 상태이므로 존재하지 않은 유저의 요청정보가 오는 경우의 테스트 추가
자바의 IllegalArgumentException을 발생시키는 예외처리에서 커스텀 예외인 UserNotFoundException으로 명시적으로 예외를 던져주기 위해 UserNotFoundException의 생성자로 Email을 받아 해당 메시지를 표시하도록 수정하였습니다.
예외테스트 시 authenticationService가 생성하지 않아서 상위레벨에서 할당하도록 변경
private UserLoginData IS_NOT_EXISTS_USER_DATA = UserLoginData.builder() | ||
.email("InvalidEmail") | ||
.password(AUTH_PASSWORD).build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로그인한 경우와 로그인 하지 않은 경우를 테스트를 작성해 주셨네요. 위에선는 AUTH_USER_DATA
을 미리 만들어 놓고 사용했었는데, 여기서는 주어진 상황이 달라짐을 명시하기 위해서 IS_NOT_EXISTS_USER_DATA
여기서 만들어주신 것 같아요.
테스트용 데이터를 미리 만들어 놓으려면 둘다 미리 만들어 놓던지 아니면은 각 Context마다 만들던지 일관성있게 맞춰주는게 좋을 것 같아요.
@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class login_메서드는 {
private AuthenticationService authenticationService = new AuthenticationService(getUserRepository(), getJwtUtil());
UserLoginData loginData;
@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class 유효한_유저로그인정보_요청를_받으면 {
@BeforeEach
void setUp() {
getUserRepository().deleteAll();
getUserRepository().save(User.builder()
.name(AUTH_NAME)
.email(AUTH_EMAIL)
.password(AUTH_PASSWORD)
.build());
loginData = UserLoginData.builder()
.email(AUTH_EMAIL)
.password(AUTH_PASSWORD)
.build();
}
@DisplayName("인증토큰을 반환한다.")
@Test
void It_returns_token() {
String accessToken = authenticationService.login(loginData);
Assertions.assertThat(accessToken).isEqualTo(VALID_TOKEN);
}
}
@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class 유효하지_않은_로그인정보를_받으면 {
@BeforeEach
void setUp() {
loginData = UserLoginData.builder()
.email("InvalidEmail")
.password(AUTH_PASSWORD).build();
}
@DisplayName("해당 정보의 회원이 존재하지 않으면 UserNotFoundException을 반환한다.")
@Test
void It_throws_UserNotFoundException() {
Assertions.assertThatThrownBy(() -> authenticationService.login(loginData)).isInstanceOf(UserNotFoundException.class);
}
}
픽스쳐 위치가 상위 컨텍스트에 공통으로 있는 부분 컨텍스트에 있는 부분 통일 되지 않아서 각 테스트 문맥에 맞도록 픽스쳐 위치 이동 하였습니다
비밀번호가 일치하지 않은 경우 예외 테스트가 존재하지않아 추가
비밀번호가 일치하지 않은 경우 예외처리가 기본 IllegalArgumentException 을 사용하여 명확하지 않은 모호하므로 커스텀에러를 추가
비밀번호가 일치하지 않은 경우를 체크하려면 해당 유저가 존재해야 하므로 해당 정보를 미리 넣어 두도록 수정
|
||
@Nested | ||
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | ||
class 유효한_유저로그인정보_요청를_받으면 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
유효하지 않은 경우도 테스트를 작성해서 스펙을 정의해줘도 좋겠네요
private UserLoginData IS_NOT_EXISTS_USER_DATA = UserLoginData.builder() | ||
.email(INVALID_EMAIL) | ||
.password(AUTH_PASSWORD).build(); | ||
private UserLoginData INVALID_PASSWORD_USER_DATA = UserLoginData.builder() | ||
.email(AUTH_EMAIL) | ||
.password(INVALID_PASSWORD) | ||
.build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 반복으로 사용되는 테스트용 데이터를 위에서 미리 정의해놓고, 여기서는 사용만 하면 이 부분이 좀 더 단순해질 것 같아요.
테스트용 데이터를 fixture라고 부르는데, 믿고 사용할 수 있는 테스트용 데이터를 미리 정의해놓는 거죠.
그런데 이러한 테스트용 데이터도 한계가 생길 수 있는데 지금처럼 테스트에 필요한 데이터가 점점 증가하기 때문이에요.
이메일이 없는 경우
이메일 형식이 잘못된 경우
비밀번호가 틀린 경우
비밀번호 형식이 틀린경우
이렇게 나열하다보면 끝없이 테스트용 데이터를 정의해줘야하고 그러다 보면 구분하기 힘들어질 수도 있어요. 그러면 아마도
테스트 데이터를 만들어주는 헬퍼 함수같은 것을 도입하고 싶은 마음이 생길거예요
테스트용 픽스처가 테스트코드와 함께 존재하는 상태에서 TestHelper 코드에 픽스쳐 분리하여 리팩터링
SessionControllerTest의 픽스쳐도 TestHelper로 대체 가능한 부분이 있기때문에 해당 부분 TestHelper 사용으로 대체
AccessTokenNotFoundException(인증토큰 없음 예외) InvalidAccessTokenException(인증토큰 유효성 예외)
누락된 @configuration 어노테이션 추가 기존의 path 패턴의 경우 해당 인증 확인 로직을 타지 않는 경우가 있기 때문에 수정
계층형 테스트 구조로 수정 하고, 인터셉터 추가에 따른 인증로직 검증 테스트 추가
유효하지 않은 유저로그인 요청인 경우의 테스트가 없었기 때문에 해당 테스트 추가 및 테스트 케이스에 따른 픽스쳐를 TestHelper 클래스에 추가 및 이동시켰습니다.
isGetMethod(request); | ||
if (isPostMethod(request) || isPatchMethod(request) || isDeleteMethod(request)) { | ||
checkAccessToken(request); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isGetMethod가 삐져나왔네요. GET 메서드일 때도 인증을 확인하는거죠?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앗 이것은 실수입니다;;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (isGetMethod(request)) {
return true;
}
if (isPostMethod(request) || isPatchMethod(request) || isDeleteMethod(request)) {
return checkAccessToken(request);
}
return true;
}
이렇게 하려고 했습니다;;;
|
||
private boolean checkAccessToken(HttpServletRequest request) throws InvalidAccessTokenException, AccessTokenNotFoundException { | ||
String authorization = request.getHeader("Authorization"); | ||
if (authorization == null) throw new AccessTokenNotFoundException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (authorization == null) {
throw new AccessTokenNotFoundException();
}
한 줄이라도 중괄호를 써주는게 더 보기 좋을 수도 있어요
try { | ||
jwtUtil.decode(accessToken); | ||
} catch (Exception e) { | ||
throw new InvalidAccessTokenException(e.getMessage()); | ||
} | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try catch문 아래에 코드가 있는 것은 이해하기 어려울 수 있어요.
try 안에 있는 값을 바로 반환하게 하는게 좋을 것 같아요
|
||
@BeforeEach | ||
void setUp() { | ||
getUserRepository().deleteAll(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
실제 db를 지우지 않도록 조심해야겠네요.
저는 테스트를 실행할 때는 Profile을 test로 하게 하고, 설정에서 테스트 DB를 사용하도록 했어요.
// build.gradle.kts
tasks.withType<Test> {
useJUnitPlatform()
environment(mapOf("SPRING_PROFILES_ACTIVE" to "test"))
}
spring:
datasource: ...
---
spring:
profiles: test
datasource:
url: jdbc:mariadb://localhost:3306/codesoomtest
username: root
password: root1234
driver-class-name: org.mariadb.jdbc.Driver
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
H2를 보통 테스트 환경에서 쓰다 보니까
인지 못했네요 실제 회사에서는
신경써서 하겠습니다!
코드를 작성 누락으로 인한 로직 수정
한 주 동안 고생하셨습니다! |
안녕하세요!
리뷰 요청드립니다
완성은 못했지만 일단 올리고
시간 날 때 추가하겠습니다
감사합니다.