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
[hoony-lab-issue12] User 도메인 구현 #19
Conversation
user_builder user_equals_and_hashCode
findAll, findById, findByEmail, save, deleteById
save_and_findAll save_and_findByEmail save_and_findById save_and_deleteById save_and_deleteById_exception
`@JsonTypeName`, `@JsonTypeInfo` : Mapping Object static `toUser` : convert DTO to Entity(User) for communicate within layers
`@JsonTypeName`, `@JsonTypeInfo` : Mapping Object static `of`: convert Entity(User) to DTO for communicate within layers
`@JsonTypeName`, `@JsonTypeInfo` : Mapping Object
joinUser : `POST /api/users/` loginUser : `POST /api/users/login`
http authorizeRequest - `/api/users/`, `/api/users/login/`
PasswordEncoder >> BCryptPasswordEncoder
POST_api_users POST_api_users_login 테스트에 대한 추가 이해와 유닛/단위 테스트 필요
- line coverage : 0.7 > 0.5 - method coverage : 1.0 > delete
`@SpringBootTest` >> `@ExtendWith(MockitoExtension.class)`
`@SpringBootTest` >> `@WebMvcTest(UserController.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.
엄살 부리셨던거에 비해서 잘 구현하신 것 같아요 💯
validation에 대한 테스트들도 추가해보시면 좋은 경험이 될 것 같아요 :)
몇가지 코멘트 남겨두었으니 확인부탁드려요~
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
|
||
@RequiredArgsConstructor |
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.
@RequiredArgsConstructor |
Constructor가 필요한 클래스일까요?
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.
보안에 ! 필요한걸 ! 생성한다 !
확인하겠습니다 ~
@CreatedDate | ||
@Column | ||
private LocalDateTime createdAt; | ||
|
||
@LastModifiedDate | ||
@Column | ||
private LocalDateTime updatedAt; | ||
|
||
@Column | ||
private LocalDateTime deletedAt; |
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.
@CreatedDate | |
@Column | |
private LocalDateTime createdAt; | |
@LastModifiedDate | |
@Column | |
private LocalDateTime updatedAt; | |
@Column | |
private LocalDateTime deletedAt; | |
@CreatedDate | |
@Column(name = "created_at", updatable = false) | |
private LocalDateTime createdAt; | |
@LastModifiedDate | |
@Column(name = "updated_at") | |
private LocalDateTime updatedAt; | |
@Column(name = "deleted_at") | |
private LocalDateTime deletedAt; |
테이블 컬럼명을 맞추어야할 것 같아요 :)
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.
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.
위 같은 상황에서 name은 안 넣어도 상관없을 것 같아요
updatable=false는 필요할수도 있겠네요
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.
저는 생각이 조금 다른 것 같아요
물론, 명명 규칙에 의해서 자동으로 컬럼 명을 만들어주지만
명명 규칙은 스프링 설정 파일에서도 변경 가능하고, 무엇이든 명시적으로 기술하는게 낫다고 보거든요
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.
저도 동일한 의견입니다.
자동으로 만들어둔 상황이 어떻게 될지 모르기때문에 과도하게 보일지라도 네이밍 규칙은 항상 명시하는게 좋다고 생각합니다.
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.
명시적... 누구에겐 취향일 수도 있겠지만 확실하게 있는 것도 좋을 것 같네요 !
public static User of(User user) { | ||
return User.builder() | ||
.email(user.getEmail()) | ||
.username(user.getUsername()) | ||
.password(user.getPassword()) | ||
.bio(user.getBio()) | ||
.image(user.getImage()) | ||
.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.
혹시 어디에 사용되는 메소드일까용?
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.
지금은 안쓰이네요 ㅠ
테스트할 때도 커버리지 안걸려서 나중에 쓰겠지하고 일단 냅뒀는데...
User
쪽 다만들고도 안쓰면 지우겠습니다 !
테이블 컬럼명을 맞추어야할 것 같아요 :) from @jinyoungchoi95 물론, 명명 규칙에 의해서 자동으로 컬럼 명을 만들어주지만, 명명 규칙은 스프링 설정 파일에서도 변경 가능하고, 무엇이든 명시적으로 기술 from @kwj1270 Co-authored-by: 최진영 <jinyoungchoi95@gmail.com>
Constructor가 필요한 클래스일까요? from jinyoungchoi95 Co-authored-by: 최진영 <jinyoungchoi95@gmail.com>
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.
깔끔하네용 👍
|
||
@PostMapping("/users") | ||
public ResponseEntity<UserResponse> joinUser(@RequestBody UserJoinRequest request) { | ||
User user = userService.save(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.
이거 save 말고 join이 좋지 않을까요? 컨트롤러랑 리퀘스트 네이밍 가져가는게 좋은 것 같아요
String token = null; | ||
|
||
return ResponseEntity.ok().body(UserResponse.of(user, 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로 해서 넣어주는거 별로 보기 안 좋은 것 같아요...
혹시 꼭 이렇게 넣어야하나요?
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.
아직 토큰 구현 안하셔서이지 않을까 싶어요
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.
추후 response에 담겨질 token을 미리 만들어보았습니다 !
when(userRepository.findById(anyLong())) | ||
.thenReturn(Optional.ofNullable(user)); | ||
|
||
//doNothing().when(userRepository).delete(any()); |
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.
얘는 지워도 되겠죠?
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.
지워도 되는 부분입니다 !
테스트 코드 부분이라서 개인적으로 고생했던 부분을 따뜻하게 담아보았습니다 😢
/** | ||
* This exception may occur if matchers are combined with raw values: | ||
* //incorrect: | ||
* someMethod(anyObject(), "raw String"); | ||
* When using matchers, all arguments have to be provided by matchers. | ||
* For example: | ||
* //correct: | ||
* someMethod(anyObject(), eq("String by matcher")); | ||
* | ||
* For more info see javadoc for Matchers 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.
이거 필요한 주석인가요??
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.
지워도 되는 부분입니다 !
테스트 코드 부분이라서 개인적으로 고생했던 부분을 따뜻하게 담아보았습니다 😢
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; | ||
|
||
@WebMvcTest(UserController.class) | ||
class UserControllerTest { |
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.
이 테스트에 뭔가 주석들이 많네요
아직 미완성 테스트인가요??
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.
미완성 테스트는 아닌 것 같습니다 😢
제가 테스트 코드를 처음 써봐서 @SpringBootTest
@WebMvcTest
@DataJpaTest
@ExtentionWith(SpringExtention.class)
@ExtentionWith(MockitoExtention.class)
@Mock
@InjectMocks
@MockBean
등 여러가지를 써보고 의미 있는 부분을 코드에 직접 기록하는 느낌으로 주석을 남겼습니다. 같이 쓰는 공간(?)에 이런걸 남겨도 되나 싶었지만 테스트라는 것을 처음 해봤고, 시간도 굉장히 많이 할애했어서 의미 있다고 생각되어 남겨봤습니다.
리뷰 달아주셔서 감사합니다 찬스님 💘
`@DataJpaTest` 에 `JpaConfig` 추가해서 `BasetimeEntity` 테스트
Codecov Report
@@ Coverage Diff @@
## hoony-lab #19 +/- ##
===============================================
- Coverage 100.00% 90.90% -9.10%
- Complexity 2 54 +52
===============================================
Files 1 10 +9
Lines 3 121 +118
Branches 0 1 +1
===============================================
+ Hits 3 110 +107
- Misses 0 8 +8
- Partials 0 3 +3
Continue to review full report at Codecov.
|
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.
코드 잘봤습니다!!! 👍 이쁜코드가 많아서 편하게 봤습니다!
|
||
public User save(UserJoinRequest request) { | ||
userRepository.findByEmail(request.getEmail()) | ||
.ifPresent(o -> new DuplicateKeyException(o.getEmail() + " already exist")); |
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.
이런식으로 익셉션을 사용하는거군요..! 너무 좋은 코드같아요 많이 배웁니다!!!... 👍 😄
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.
👍
() -> assertEquals(PASSWORD, user.getPassword()), | ||
() -> assertEquals(BIO, user.getBio()), | ||
() -> assertEquals(IMAGE, user.getImage()) | ||
); |
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.
궁금한 점이 있는데용 객체를 빌드하는 단계까지 테스트하는 의의를 여쭤볼 수 있을까요?
제가 객체빌드 또는 생성까지 테스트하는건 처음봐서요! ➰
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.
User
가 Builder
로 잘 만들어지나 한 번 테스트해봤어요
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.
깔끔하네요.
LGTM 👍 |
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.
깔끔한 코드네요 💯
몇 가지 코멘트 남겼으니 확인 부탁드려요 :)
String token = null; | ||
|
||
return ResponseEntity.ok().body(UserResponse.of(user, 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.
아직 토큰 구현 안하셔서이지 않을까 싶어요
|
||
@Builder | ||
public User(Long id, String email, String username, String password, String bio, String image) { | ||
this.id = id; |
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.
GenerationType.IDENTITY
의 경우 id를 제외한 빌더를 사용해도 상관없으니 참고 부탁해요 :)
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.
아직 토큰 구현 안하셔서이지 않을까 싶어요
넵 맞습니다
GenerationType.IDENTITY의 경우 id를 제외한 빌더를 사용해도 상관없으니 참고 부탁해요 :)
DB쪽(?) 에서 케어하는 부분이라 상관없나보군요 🆗
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.
위에꺼는 왜 코멘트를 달 수 있는 부분이 안보일까요 ㅠㅠ
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.
이전에 코멘트 달았는데 outdate 되어서 그런것 같아요 ㅎㅎ
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
User user = (User) o; | ||
return Objects.equals(email, user.email); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(email); | ||
} |
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 이 나올 수 있어서 email()
로 사용해주면 좋을 것 같아요
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.
프록시 객체
라는 걸 확인해보도록 하겠습니다 !
User user = userRepository.findByEmail(request.getEmail()) | ||
.orElseThrow(() -> new NoSuchElementException(request.getEmail() + " not found")); |
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.
findByEmail이 중복되는 것 같은데 메서드로 분리해서 재사용도 좋을 것 같네요!
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(!user.matchesPassword(request.getPassword(), passwordEncoder)) { | ||
throw new NoSuchElementException(request.getPassword() + " wrong wrong wrong triple wrong" + user.getPassword()); | ||
} |
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.
에러 검증 로직이 login()
이라는 비즈니스 로직과는 조금 거리가 있다고 생각해요
이럴 경우 메서드로 분리해서 내부가 어떻게 돌아가는지 감추는 것도 좋을 것 같습니다
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.
Service > login
에는 비즈니스 로직만...(?)
메서드로 분리
라는게 클린 코드
에 해당 하는 부분이라고 생각하면 될까요 ?
참고할수있는 키워드 하나만 주시면 더 공부해보겠습니다 !
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.
사람마다 기준점은 다르지만 저 같은 경우
login()
은 순수하게 로그인 기능
만을 표현해야 된다고 생각해요
저 같은 경우 이런 예외를 던지는 부가 기능
부분에 있어서 최대한 메서드로 분리시키려고 해요
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.
에러 발생을 도메인단으로 보내도 될 것 같은데 이 부분도 고려해볼만 하겠네요
제 리뷰는 단순히 저의 견해이고 아직 저도 좋은 방법은 못찾아서 그냥 참고만 부탁해요
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.
감사합니다 뇌섹 우지님 💘
User Builder 관련 수정 User, UserTest, UserRepositoryTest, UserServiceTest, UserControllerTest 수정 idea-by: kwj1270 Co-Authored-By: 최진영 <jinyoungchoi95@gmail.com>
`findByEmail이 중복되는 것 같은데 메서드로 분리해서 재사용도 좋을 것 같네요!` Co-Authored-By: KimWooJae <50267433+kwj1270@users.noreply.github.com>
> `메서드로 분리해서 내부가 어떻게 돌아가는지 감추는 것` `validateMatchesPassword` 메소드 추가하여 비밀번호 확인 로직 이동 Co-Authored-By: KimWooJae <50267433+kwj1270@users.noreply.github.com>
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.
의견만 하나 드렸습니다~ LGTM 👍
import java.util.NoSuchElementException; | ||
|
||
@RequiredArgsConstructor | ||
@Transactional |
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.
@Transactional
은 보통 Class단에 어노테이션으로 먹일 때에는 readOnly = true, CUD 메소드에 먹일 때 readOnly = false로 먹이니 참고만 해주셔용
Issue: #12
작업 내용
POST /api/users/login
(No authentication)POST /api/users
(No authentication)악생성/변경 로직
개인 코멘트
hello world
는 참 힘들다What is TDD and DDD