Facts
•
스프링 3기 6주차 미션 진행 및 피드백 학습
◦
JWT 학습
◦
커스텀 애노테이션을 이용한 ArgumentResolver 사용하기 학습
◦
스프링에서 사용하는 각종 테스트 애노테이션 학습
◦
다양한 Test Double 중 FakeObject 사용및 학습
◦
주석에 대한 고민
◦
@Value 애노테이션 학습
◦
한 번 더 추상화!
•
우아한테크캠프 PRO 신청서 제출
•
좋은 질문하기에 대한 고민
•
넥스트스텝 블랙커피 블로그 스터디 시작
Feelings
스프링 3기 6주차 미션 진행 및 피드백 학습
JWT부터는 나도 따로 개인적으로 학습을 깊게 해본적도 없을 뿐더러 실무에서도 JWT를 사용하지 않기에 두근거리는 마음으로 강의를 들었다.
너무 오래되고 2주차 정도부터 야근으로 인해 참가하지못한 프로그래머스 백엔드 스터디에서의 JWT 학습은 너무 오래되어 기억보단 감정만 남은 상태이기에 당시의 미숙했던 내게 남았던 감정은 그저 마냥 '어렵고 이해가 안된다' 였었다. 그렇기에 이번 6주차 미션에서도 드디어 장벽이 다가오는건가? 생각을 하면서 영상의 재생버튼을 눌렀다.
그런데 다행히도 이해하기 힘들거나 어려운 부분은 없었는데, 이는 내가 미숙하고 아는게 없었던 몇년 전과 지금과는 이해력의 차이가 있기 때문이 아닐까 추측한다.
각설하고, 이번 6주차에서도 많은 내용을 학습하고 시도해봤다. 그리고 오랜만에 기술 포스팅도 하는 시간을 가졌는데, 이는 리뷰어님의 @Value 애노테이션에 대해 설명해보라는 말로 시작되어 스프링 테스트 애노테이션들을 사용하면서 생각대로 되지 않기에 학습을 시작하면서 해당 내용에 대한 포스팅까지 작성하면서 오랜만에 블로그에 회고록 외의 기술 포스팅을 한 것 같다.
한 때 하루에 1개~3개이상의 포스팅을 할 때는 오히려 내가 지금 너무 복사 붙혀넣기만하고 시간만 버리는 지식자랑이 아닌가 생각을 했었다. 내가 작성한 글도 제대로 기억을 못해서 관련 키워드가 나와도 바로 답변이 나오는게 아닌 내 블로그 글을 찾아봐야 했기에 글을 쓸수록 오히려 자신감이 줄어들고 조급함이 커져갔다.
하지만, 이번 6주차 두 개의 포스팅을 하면서 느낀 점은 이러니 저러니해도 포스팅을 하기 위해선 내용을 요약 정리해야하고, 정말 대충 쓰는 글이 아니라면 예제코드부터 3줄 요약할 수 있게끔 고민을 계속 하면서 내용을 되새겨야하는데, 이 과정자체가 학습이라는 것이다.
그리고 나중에라도 블로그 글을 찾아보면서 바로 대답할 수있다는 것 자체가 이해는 하고있다는 것이 아닐까? 물론, 계속 복습하고 내 포스팅을 내가 다회독 하면서 글을 보지 않고도 대답 할 수준이 되는 것이 중요하겠지만 말이다
우아한 테크코스 PRO 신청서 제출
저번 기수에는 탈락했던 우아한 테크코스 프로 3기에 또 신청서를 제출했다.
기존 만4년차 이상을 모았던것과 달리 만 3년차가 조건인데 나는 이제 2.5년차 정도이니 예전보단 낫다 생각이 들었고, 은근히 넥스트스텝 과정을 다 수강하면서 모두 다 수료를 잘 마쳤기에 자신이 있었는데 떨어졌다는게 자존심이 상하기도 해서 자신감 회복을 위해서이기도 하다.
마친, 아는 분으로부터 합격자의 신청서를 보고 유튜브로 진행하는 설명회를 들어보니 내가 왜 떨어졌는지 대략 감을 잡을 수 있었다.
나는 신청서의 약 4개의 항목에 모든 항목을 2~3줄로 단답형으로 요약해서 작성을 했다. 이전에 듣기를 어짜피 자소서같은건 잘 안보고 중요한건 기술이니 내가 뭘 했고 어떤 기술을 가지고 있는지 사실만 간략하게 작성을 했었는데, 이렇게 짧게 작성한건 거의 보지도 않고 넘긴다고 하는걸 듣고, 합격한 분의 신청서를 보니 나보다 스펙이나 경험도 많은데도 훨씬 많고 정성스러운 신청서였다.
그래서 이번에는 나도 그 사이겪은 경험과 학습과정 그리고 요약이 아닌 서술형으로 신청서를 작성하여 제출했다.
과정 시작일도 코드숨 과정과 프로그래머스 알고리즘 스터디가 끝나는 시기와 맞으니 스프링 3기를 무사히 잘 수료하고 우아한테크코스PRO도 잘 진행하면 좋지 않을까? 생각을 해본다.
좋은 질문하기에 대한 고민
결론부터 말해서 나는 눈치를 많이본다. 그것도 아주 많이...
그래서 질문을 할 때도 혼자 대본을 쓰고 소설을 쓰고 그걸 수정, 재수정, 최종수정을 거치고 거쳐 질문을 한다. 그리고 질문이나 답변을 작성할때는 작성하면서 혹시 몰라 상대방이 다시 질문하고나 반박하거나 할 말에 대해 예상해보고 그것도 같이 작성을 한다.
그러다보면 글은 주절거리듯이 장황하고 핵심을 찾기 힘들어진다.
이에 대해서 최근 리뷰어님과도 심도깊게 이야기를 나누고, 이런 문제는 많이 해결되었다.
물론, 속으로는 좀 더 다시 작성을 해야하지 않을까? 하지만, 이전처럼 고민할 시간에 등록 버튼을 눌러버린다.
그리고 대신 요즘은 이런 고민을 한다.
좋은 질문은 어떻게 해야할까?
질문을 하다보면 내가 설명을 못하는 것일 수도 있고, 상대방이 내 질문을 잘 이해하지 못하는 걸수도 있지만, 한 번의 질문으로 이슈에 접근해서 해결되기 보다는 얘기가 뱅뱅돌면서 계속 설명을 하면서 서로 시간낭비가 되는 경우가 종종 발생한다. 또한, 질문은 질문자 뿐 아니라 답변자와 이 질문을 보는 모두에게 인사이트를 주고 좋은 영향을 줄 수 있으면 최고인데, 이런 질문을 어떻게 해야할지 고민하게 된다.
넥스트스텝 블랙커피 블로그 스터디 8기 시작
나는 작년부터 넥스트스텝의 임동준님이 주관하는 블로그 스터디를 1기부터 7기까지 모두 참가하고 이번에 8기까지 참가하게 되었다. 스터디 주제는 거창하지 않고 일주일에 하나, 한달에 한 주의 버퍼기간을 제외하고 3번은 글을 쓰는 것이다. 물론 주제는 자유이다.
개발 얘기도 좋지만 그냥 운동하고 먹고, 데이트하고 논 이야기를 써도 된다.
어떤 분은 군대에서 파견을 가서 레바논과 세계를 경험했던 이야기를 쓰고, 나는 신혼부부 전세자금 대출부터 공부나 어두운 내 고민들까지 작성을 했다. 어떤 분은 카카오에 입사하기까지의 과정을 적은분도 있다.
어떻게 보면 그냥 교양 스터디이고, 개발역량에는 도움이 안 될 것처럼 보인다.
하지만, 적어도 나는 엄청 많은 도움을 얻었다. 애초에 기술 블로그를 계속 하고는 싶었지만, 성실함도 부족하고, 글을 작성하고자 하는열의나 내 생각을 글로 작성하는 습관도 없기에 한 두줄짜리 문장은 작성해도 글을 길게 풀어갈 능력은 부족해서 블로그를 시도만 여러번 하고 성공한 적이 없었다. 그런 내게 블로그스터디는 약간은 강제적으로 글을 쓰게 만들었고 다른 사람의 글을 보게끔 하면서 계속해서 글을 쓰게하는 일종의 습관을 만들도록 해줬다.
하지만, 스터디를 그만두면 바로 게을러지는 모습이 생기기에 계속해서 신청을 하게되었고 어느덧 8기까지 왔는데, 이번에는 코드숨에서 작성하는 주간회고를 같이 사용할 수 있으니 좀 더 편한 마음으로 참가하게 되었다.
Finding
스프링 3기 6주차 미션 진행 및 피드백 학습
JWT 학습
JWT 기능을 사용하면서도 해당 토큰에 대한 지식과 이해가 부족하기에 별도로 학습하며 포스팅을 해보았다.
커스텀 애노테이션을 이용한 ArgumentResolver 사용하기 학습
매 번 request에서 JWT를 꺼내와서 추출하는 메서드를 호출하는 과정을 작성하는것은 번거로운것 같아 MethodArgumentResolver를 구현해서 커스텀 애노테이션 @AuthenticationPrincipal 을 만들어 자동으로 주입될 수 있도록 구현해줬다.
스프링에서 사용하는 각종 테스트 애노테이션 학습
처음에는 given/verify를 사용하지 않고 최대한 실제 혹은 FakeObject를 사용하며 테스트를 진행하고 싶었는데, 아무생각없이 @WebMvcTest나 @DataJpaTest 를 사용해서는 내가 원하는 빈들이 등록되지 않아서 테스트를 진행할 수 없었다.
그렇다고 @SpringBootTest를 사용하면 모든 빈을 등록해서 주입은 가능했지만 이런 가벼운 테스트에서도 속도가 이래서야 실무에서 규모가 있는 테스트를 진행하면 테스트 비용소모가 너무커서 문제라고 생각하여 학습을 진행했다.
다양한 Test Double 중 FakeObject 사용및 학습
4주차 과정을 진행하며 학습한 내용 중 Test Double과 여러 Test Double 중 Fake Object에 대해 학습을 했었다. 그리고 이를 이번 기회에 사용해보면서 given/verify를 사용하지 않게끔 시도를 해보았다.
주석에 대한 고민
매번 주석에 대한 리뷰를 받는다. 처음엔 그냥 이게 더 문장이 자연스러운가보다 하면서 수정하고 넘어갔다. 하지만, 같은 리뷰가 반복된다면 분명 반복되지 않게끔 노력할 필요가 있다.
그래서 리뷰들을 되짚으며 고민을 해 본 결과 100%정답까진 아니지만 몇가지의 규칙을 찾았다.
•
주석에서 중요한것은 main description, @return, @throws 이다. 특수한 경우가 아니라면, 아니 특수한경우라도 이 세가지 주석은 public 메서드에는 작성해주도록 하자.
•
주석은 구구절절 설명하듯 주절거리게 작성하지 말아라.
•
주석이 로직이나 객체에 의존성을 가지면 변경에 취약해진다.
•
주석이 세세할수록 내부 로직을 외부에 공개하게 된다. 이는 보안 뿐 아니라 내부로직이 변경되면 주석도 모두 변경해야 하는 위험이 있다. 예를 들어 다음 주석을 보자.
/**
* 로그인 정보를 조회해서 토큰을 발급한다.
*
* @param identifier 이메일과 비밀번호가 포함된 로그인 정보
* @return 토큰
* @throws LoginDataNotMatchedException 아이디 혹은 비밀번호가 유효하지 않다.
*/
public String login(Identifier identifier) { ... }
Java
복사
→ @param identifier 이메일과 비밀번호가 포함된 로그인 정보
: Identifier가 이메일과 비밀번호가 아닌 이름과 주민등록번호로 바뀐다면 주석도 변경해야하며 이를 놓칠경우 인수인계받는 다음 개발자는 혼란에 빠져 잘못된 로직을 작성할 수 있다. 파라미터 타입의 이름의 뜻 그대로 식별자 정보(혹은 로그인 정보) 정도면 충분하다.
→ @return 토큰
: 포괄적인것은 좋지만 주의하자. 해당 메서드는 로그인 메서드이다. 즉, 내부가 어떻게 바뀌더라도 결국 로그인을 해서 반환하는 토큰은 로그인 후 로그인 했다는 증거 토큰이기에 접속 토큰 혹은 인증 토큰이라고 작성해줘도 된다.
→ @throws LoginDataNotMatchedException 아이디 혹은 비밀번호가 유효하지 않다.
: 예외를 설명하는것은 좋다. 하지만 이 역시 너무 자세한 설명이다. 지금은 아이디 및 비밀번호가 유효하지 않은 경우(아이디가 등록되지 않았거나, 비밀번호가 입력되지 않았거나 해당 아이디와 비밀번호가 일치하지 않거나) 예외를 발생한다고 했지만, 위에서 말했듯 식별자 정보가 이름과 주민등록번호로 바뀔 경우 해당 예외가 발생하는 설명은 아이디와 비밀번호가 아니다. 즉, 내용이 바뀔 경우 혹은 추가될 경우 주석을 변경해야 한다.
좀 더 포괄적으로 인증에 실패한 경우 정도로 작성해주는것으로도 충분하다.
@Value 애노테이션 학습
자주 사용하지는 않았기에 크게 인지하지 않았던 @Value 애노테이션.
이번 애노테이션을 학습하며 좋았던 점은, 해당 애노테이션에 대한 학습도 있지만, SpEL과 스타일 프로퍼티 치환자 방식 중 주로 스타일 프로퍼티 치환자 방식만 사용했었는데 능동적인 SpEL에 대해 알게 된 점이다.
한 번 더 추상화!
이번에 MethodArgumentResolver를 구현하면서 내가 주입받도록 한 객체는 LoginForm이라는 객체였다.
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class LoginForm {
@NotBlank
@Size(min = 3)
private String email;
@NotBlank
@Size(min = 4, max = 1024)
private String password;
public static LoginForm of(String email, String password) {
return new LoginForm(email, password);
}
}
Java
복사
애초에 간단한 구조이기에 문제는 딱히 없었다. 그런데 리뷰에서 이런 내용을 받게 되었다.
이 말을 듣고 dto와 domain패키지를 잘 살펴보니 다음과 같은 객체에서 공통적으로 email과 password를 사용하고 있었다.
•
UserRegistrationData
•
LoginForm
•
User
또한 email,password 필드는 모두 공통으로 로그인에 사용되는 정보이다. 그럼 리뷰어님이 말씀해주신대로 인터페이스를 만들 수 있을 것 같다. 뭐가 좋을까.... 생각해본 결과 로그인 정보도 좋았지만 좀 더 포괄적으로 생각해보면 로그인에 사용되는 정보를 사용자를 식별할 수 있는 정보이기에 식별자 정보가 어울린다. 그렇기에 결정된 인터페이스 명은 Identifier
public interface Identifier extends Serializable {
String getEmail();
String getPassword();
}
Java
복사
이를 각각의 객체에서 상속하도록하여 Service Layer에서는 Identifier 객체를 인자로 받게끔하여 다형성을 적용해 메서드의 유연함을 늘렸다.
// Identifier를 인자로 받음으로써 식별자 정보를 다형성으로 받아 유연함이 늘었다.
public String login(Identifier identifier) { ... }
Java
복사
그런데 여기서 다음 리뷰를 받게되며 한 번 더 추상화를 할 수 있다는 사실을 깨달았다.
다른건 다 이해를 했는데 findByEmailAndDeletedIsFalse를 오버로드한 default method가 무슨 말인지 잘 이해가 안되었는데, 코드를 작성하다보니 자연스레 이해하게 되었고,
Email과 Password를 각각 EamilSupplier, PasswordSupplier 인터페이스로 분리하여 작성을 해 보았다.
/**
* 이메일을 제공한다.
*/
@FunctionalInterface
public interface EmailSupplier {
String getEmail();
}
Java
복사
EmailSupplier
/**
* 비밀번호를 제공한다.
*/
@FunctionalInterface
public interface PasswordSupplier {
String getPassword();
}
Java
복사
PasswordSupplier
/**
* 식별자 정보를 관리한다.
*/
public interface Identifier extends Serializable,
EmailSupplier, PasswordSupplier {
}
Java
복사
수정된 Identifier
public interface UserRepository {
Optional<User> findByEmailAndDeletedIsFalse(String email);
default Optional<User> findByEmailAndDeletedIsFalse(EmailSupplier supplier) {
return this.findByEmailAndDeletedIsFalse(supplier.getEmail());
}
}
Java
복사
findByEmailAndDeletedIsFalse를 오버로딩한 findByEmailAndDeletedIsFalse(EmailSupplier supplier) default method
물론, 여기서 PasswordSupplier의 @FunctionalInterface를 빼고 password 필드의 암호화, 복호화 메서드를 추가할 수도 있고, 암호화와 복호화에 사용할 알고리즘을 변경할 수 있도록 Strategy Pattern을 적용해서 EncrypAlgorithm 인터페이스를 만들어 제공 할 수도 있겠지만, 금주의 미션의 주제와는 벗어난다 생각하여 생략한다.
Affirmation
추석 주간에는 기존에 배웠던 내용들 복습하고 리마인드 하는 시간을 가지며 그 다음주차에 할 내용들을 작성한다.
•
인프런 - 김영한님의 MVC2편 마무리
•
스프링 3기 6주차 미션 과제풀이 영상 시청
•
스프링 3기 7주차 미션 시작
•
자료구조와 알고리즘 스터디 준비 학습