Skip to content
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

Merged
merged 31 commits into from Aug 13, 2021
Merged

Conversation

hoony-lab
Copy link
Member

Issue: #12

작업 내용

  • Project Setting
  • User : Entity, Repository, Service, Controller, DTO
  • Authentication : POST /api/users/login (No authentication)
    • token 제외
  • Registration : POST /api/users (No authentication)
    • token 제외
  • Test

생성/변경 로직

개인 코멘트

  • 처음부터 만들기는 참 힘들다
  • JUnit5 의 hello world는 참 힘들다
  • What is TDD and DDD

hoony-lab and others added 25 commits August 1, 2021 22:49
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)`
@hoony-lab hoony-lab requested a review from sujl95 August 7, 2021 11:41
@hoony-lab hoony-lab self-assigned this Aug 7, 2021
@hoony-lab hoony-lab added the hoony-lab 후니의 이슈 label Aug 7, 2021
Copy link
Member

@jinyoungchoi95 jinyoungchoi95 left a 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@RequiredArgsConstructor

Constructor가 필요한 클래스일까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

보안에 ! 필요한걸 ! 생성한다 !

확인하겠습니다 ~

Comment on lines 18 to 27
@CreatedDate
@Column
private LocalDateTime createdAt;

@LastModifiedDate
@Column
private LocalDateTime updatedAt;

@Column
private LocalDateTime deletedAt;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@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;

테이블 컬럼명을 맞추어야할 것 같아요 :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

앱 실행하고 h2-console에서 확인하면 이렇게 나오는데 혹시 이렇게 저 처럼 알아서 넣어지길 바라는건 틀린걸까요 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위 같은 상황에서 name은 안 넣어도 상관없을 것 같아요
updatable=false는 필요할수도 있겠네요

Copy link

@kwj1270 kwj1270 Aug 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 생각이 조금 다른 것 같아요
물론, 명명 규칙에 의해서 자동으로 컬럼 명을 만들어주지만
명명 규칙은 스프링 설정 파일에서도 변경 가능하고, 무엇이든 명시적으로 기술하는게 낫다고 보거든요

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 동일한 의견입니다.
자동으로 만들어둔 상황이 어떻게 될지 모르기때문에 과도하게 보일지라도 네이밍 규칙은 항상 명시하는게 좋다고 생각합니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명시적... 누구에겐 취향일 수도 있겠지만 확실하게 있는 것도 좋을 것 같네요 !

Comment on lines +48 to +56
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();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 어디에 사용되는 메소드일까용?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금은 안쓰이네요 ㅠ
테스트할 때도 커버리지 안걸려서 나중에 쓰겠지하고 일단 냅뒀는데...
User 쪽 다만들고도 안쓰면 지우겠습니다 !

@hoony-lab hoony-lab changed the title [hoony-lab #12] User 도메인 구현 [hoony-lab-issue12] User 도메인 구현 Aug 8, 2021
hoony-lab and others added 2 commits August 9, 2021 01:05
테이블 컬럼명을 맞추어야할 것 같아요 :)
from @jinyoungchoi95

물론, 명명 규칙에 의해서 자동으로 컬럼 명을 만들어주지만, 명명 규칙은 스프링 설정 파일에서도 변경 가능하고, 무엇이든 명시적으로 기술
from @kwj1270

Co-authored-by: 최진영 <jinyoungchoi95@gmail.com>
Constructor가 필요한 클래스일까요?
from jinyoungchoi95

Co-authored-by: 최진영 <jinyoungchoi95@gmail.com>
Copy link
Contributor

@chance0523 chance0523 left a 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 save 말고 join이 좋지 않을까요? 컨트롤러랑 리퀘스트 네이밍 가져가는게 좋은 것 같아요

Comment on lines +25 to +27
String token = null;

return ResponseEntity.ok().body(UserResponse.of(user, token));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 null로 해서 넣어주는거 별로 보기 안 좋은 것 같아요...
혹시 꼭 이렇게 넣어야하나요?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아직 토큰 구현 안하셔서이지 않을까 싶어요

Copy link
Member Author

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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

얘는 지워도 되겠죠?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지워도 되는 부분입니다 !
테스트 코드 부분이라서 개인적으로 고생했던 부분을 따뜻하게 담아보았습니다 😢

Comment on lines +238 to +248
/**
* 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.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 필요한 주석인가요??

Copy link
Member Author

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 테스트에 뭔가 주석들이 많네요
아직 미완성 테스트인가요??

Copy link
Member Author

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-commenter
Copy link

codecov-commenter commented Aug 11, 2021

Codecov Report

Merging #19 (51e51e6) into hoony-lab (f7b58eb) will decrease coverage by 9.09%.
The diff coverage is 90.67%.

Impacted file tree graph

@@               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     
Impacted Files Coverage Δ
...ain/java/com/study/realworld/user/domain/User.java 70.00% <70.00%> (ø)
.../com/study/realworld/user/service/UserService.java 89.47% <89.47%> (ø)
...ain/java/com/study/realworld/config/JpaConfig.java 100.00% <100.00%> (ø)
...ava/com/study/realworld/config/SecurityConfig.java 100.00% <100.00%> (ø)
.../study/realworld/global/common/BaseTimeEntity.java 100.00% <100.00%> (ø)
.../com/study/realworld/user/dto/UserJoinRequest.java 100.00% <100.00%> (ø)
...com/study/realworld/user/dto/UserLoginRequest.java 100.00% <100.00%> (ø)
...ava/com/study/realworld/user/dto/UserResponse.java 100.00% <100.00%> (ø)
...a/com/study/realworld/user/web/UserController.java 100.00% <100.00%> (ø)
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f7b58eb...51e51e6. Read the comment docs.

Copy link

@com8599 com8599 left a 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"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런식으로 익셉션을 사용하는거군요..! 너무 좋은 코드같아요 많이 배웁니다!!!... 👍 😄

Copy link
Member Author

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())
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

궁금한 점이 있는데용 객체를 빌드하는 단계까지 테스트하는 의의를 여쭤볼 수 있을까요?
제가 객체빌드 또는 생성까지 테스트하는건 처음봐서요! ➰

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UserBuilder로 잘 만들어지나 한 번 테스트해봤어요

Copy link
Member

@DolphaGo DolphaGo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

깔끔하네요.

@hoony-lab
Copy link
Member Author

깔끔하네요.

LGTM 👍

Copy link

@kwj1270 kwj1270 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

깔끔한 코드네요 💯
몇 가지 코멘트 남겼으니 확인 부탁드려요 :)

Comment on lines +25 to +27
String token = null;

return ResponseEntity.ok().body(UserResponse.of(user, token));
Copy link

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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GenerationType.IDENTITY의 경우 id를 제외한 빌더를 사용해도 상관없으니 참고 부탁해요 :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아직 토큰 구현 안하셔서이지 않을까 싶어요

넵 맞습니다

GenerationType.IDENTITY의 경우 id를 제외한 빌더를 사용해도 상관없으니 참고 부탁해요 :)

DB쪽(?) 에서 케어하는 부분이라 상관없나보군요 🆗

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에꺼는 왜 코멘트를 달 수 있는 부분이 안보일까요 ㅠㅠ

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이전에 코멘트 달았는데 outdate 되어서 그런것 같아요 ㅎㅎ

Comment on lines +67 to +77
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);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프록시 객체 활용시 값이 null 이 나올 수 있어서 email()로 사용해주면 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프록시 객체 라는 걸 확인해보도록 하겠습니다 !

Comment on lines 61 to 62
User user = userRepository.findByEmail(request.getEmail())
.orElseThrow(() -> new NoSuchElementException(request.getEmail() + " not found"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findByEmail이 중복되는 것 같은데 메서드로 분리해서 재사용도 좋을 것 같네요!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 64 to 66
if(!user.matchesPassword(request.getPassword(), passwordEncoder)) {
throw new NoSuchElementException(request.getPassword() + " wrong wrong wrong triple wrong" + user.getPassword());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러 검증 로직이 login()이라는 비즈니스 로직과는 조금 거리가 있다고 생각해요
이럴 경우 메서드로 분리해서 내부가 어떻게 돌아가는지 감추는 것도 좋을 것 같습니다

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Service > login 에는 비즈니스 로직만...(?)
메서드로 분리 라는게 클린 코드 에 해당 하는 부분이라고 생각하면 될까요 ?

참고할수있는 키워드 하나만 주시면 더 공부해보겠습니다 !

Copy link

@kwj1270 kwj1270 Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사람마다 기준점은 다르지만 저 같은 경우
login()은 순수하게 로그인 기능만을 표현해야 된다고 생각해요
저 같은 경우 이런 예외를 던지는 부가 기능 부분에 있어서 최대한 메서드로 분리시키려고 해요

Copy link

@kwj1270 kwj1270 Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러 발생을 도메인단으로 보내도 될 것 같은데 이 부분도 고려해볼만 하겠네요
제 리뷰는 단순히 저의 견해이고 아직 저도 좋은 방법은 못찾아서 그냥 참고만 부탁해요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다 뇌섹 우지님 💘

hoony-lab and others added 3 commits August 12, 2021 18:22
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>
@hoony-lab hoony-lab merged commit b5adfca into hoony-lab Aug 13, 2021
Copy link

@povia povia left a 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
Copy link

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로 먹이니 참고만 해주셔용

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hoony-lab 후니의 이슈
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants