wafflestudio / seminar-2021

2021 Rookies 세미나
47 stars 110 forks source link

과제2를 진행하며 생긴 의문점들을 모아서 질문드립니다. #527

Closed yg-moon closed 3 years ago

yg-moon commented 3 years ago

과제2를 진행하며 생긴 의문점들을 모아서 질문 드립니다. 이미 PR을 올린 상태고, 질문 개수도 많은 만큼 천천히 답변해주셔도 괜찮을 것 같습니다!

구현 방식

어떻게든 돌아가게는 만들었지만, 더 좋은 방법은 없을지, 어떤 방식이 일반적으로 권장되는 방법인지에 대한 질문들입니다.

  1. 모델의 필드에 직접 접근해서 값을 바꾸는 것은 괜찮은가요? 가령 user나 seminar의 내용을 edit할 때, 수정해야 하는 필드를 var로 바꾸고 직접 접근해서 수정했습니다. 혹시 필드는 최대한 val로 두고 getter나 setter를 사용해서 변경하는 것이 권장되는 방법인지 궁금합니다.

  2. Exception의 경우 최대한 세분화해서 다 만들어주는게 좋은 방법인가요? 특히 8번에서 다양한 경우의 익셉션이 생기는데, 모두 다른 익셉션을 만들어주는 것이 권장되는 것인가요? ErrorType 변수도 최대한 세분화 하는 것이 좋은지 궁금합니다.

  3. RequestBody에서 값이 비어있는지 검증하는 좋은 방법이 있나요? "Body 값이 비어있는 경우는 무시하고" 라는 스펙이 여러 번 있었던 것으로 기억하는데요, 저는 DTO에 기본값을 넣고 입력값이 그것과 똑같으면 빈 것으로 인식하도록 구현했는데 혹시 다른 좋은 방법이 있는지 궁금합니다.

  4. Null 관련 처리는 어떤 기준이나 가이드 같은 것이 있나요? 기본값이 없어서 생기는 오류를 해결하거나, 여타 구현 중 발생하는 오류를 모면하기 위해 ? 타입을 남발했던 것 같습니다. 그러다보니 멤버에 접근할 때도 ?. 로 해야 하고, 함수에도 !! 를 써야 하는 등 코드가 점점 불필요하게 지저분해지는 느낌이 들었습니다. 혹시 null 관련해서 어떻게 써야한다, 이럴 때는 쓰면 안 된다는 가이드라인 같은 것이 있을까요?

(억지로 쓰는 상황 예시: 엔티티를 create 할 때 모든 필드를 다 인자로 넘겨주길 바라는데, 다른 엔티티와 연결된 정보는 마음대로 넘기면 안 될 것 같아서 타입에 ? 를 붙이고 기본값을 null로 설정하여 오류 메시지를 해결.)

스프링

  1. createdAt, updatedAt은 원래 알아서 시간을 기록하는 것이 맞나요? DataGrip에서는 항상 null로 나와서 일단 @createdAt에만 LocalDateTime.now()를 써주긴 했는데, updateAt은 여전히 제대로 동작하지 않는 것 같습니다. 주석처리 해두셨던 @EntityListeners를 추가해봐도 여전히 똑같은데 원래 알아서 시간을 기록하는게 맞나요?

  2. Transactional 어노테이션은 언제 쓰는 것인가요? Java/Spring에서는 Transactional을 달아서 사용하던데, dirty checking도 자동으로 되어서 save도 호출하지 않아도 되어 편해 보였던 것 같습니다. 혹시 이번 과제에는 적합하지 않거나, 코틀린에서는 잘 쓰지 않는 방식인가요?

기타

  1. 포스트맨에서 검증할 때 매번 토큰을 일일이 넣어주는게 최선의 방법인가요? 테스트 코드를 짠다면 해결 되겠지만, 이번처럼 포스트맨으로만 검증하는 경우 매번 sign-up을 하고 그 토큰을 복사하여 다시 헤더에 붙여넣고 API를 테스트 하는 것이 최선의 방법인가요? 매번 너무 번거로워서 궁금했습니다.

  2. 채점이 끝난 이후 모범답안 코드를 공개 해주실 수 있나요? 사실 이런 자잘한 의문점들은 대부분 좋은 컨벤션을 따라서 잘 짠 코드를 보는 것만으로도 많이 해소된다고 생각합니다. 혹시 이후 과제와 연관되어서 공개하기 어렵다면, 세미나가 모두 끝난 이후에라도 해주실 수 있는지 여쭤봅니다!

pullup-dip-burpee commented 3 years ago

5번 질문의 경우, class SeminarApplication 에 @EnableJpaAuditing 를 붙이시고, open class BaseTimeEntity에 @EntityListeners(AuditingEntityListener::class)가 주석처리 되어있는데 주석처리를 해제하시면 createdAt, updatedAt 자동 기록이 정상작동합니다!

pullup-dip-burpee commented 3 years ago

7번 질문의 경우, 스택오버플로우의 누군가 가 올린 질문에 대한 답변에 보면, On Postman: Then make a Global variable in postman as jwt_token = TOKEN_STRING. 이런 식으로 global 변수를 만들어서 사용하면 조금 편하다고 하네요!

pullup-dip-burpee commented 3 years ago

또 저도 1번이 정말 궁금한데, 추가로 연관관계 편의 메소드를 만들고 싶은 상황도 질문드립니다. 가령 ParticipantProfile과 Seminar 사이의 ManyToMany를 대신하는 어떤 엔티티(저는 SeminarParticipant라고 지었는데)를 생성하려 할 때 생기는 leaking this warning을 없애기 위해

질문드립니다!

pullup-dip-burpee commented 3 years ago

8번은 이 분 예시 코드가 있는 것 같네요!

yg-moon commented 3 years ago

8번은 이 분 예시 코드가 있는 것 같네요!

오 이건 장고 코드네요! 스프링 버전도 제공되면 좋을 것 같습니다.

WallabyG commented 3 years ago

1번 질문의 경우 코틀린 코드에서 .property로 접근하면 자동으로 생성된 getter와 setter를 사용하는 것으로 알고 있습니다. val은 read-only로, 한 번 초기화하면 수정할 방법이 없어서 setter로 접근해도 안 될 것 같고, var을 사용하는 게 맞는 것 같아요.

pullup-dip-burpee commented 3 years ago

3번 질문이 어떤 의미인지 제가 잘 이해하지 못해서 여쭤봅니다! @RequestBody를 통해 들어오는 값들 중 없는 건 null로 처리되는 것으로 아는데 혹시 DTO에 기본값을 넣는 경우가 어떤 것인지 설명 부탁드려도 될까요?

yg-moon commented 3 years ago

3번 질문이 어떤 의미인지 제가 잘 이해하지 못해서 여쭤봅니다! @requestbody를 통해 들어오는 값들 중 없는 건 null로 처리되는 것으로 아는데 혹시 DTO에 기본값을 넣는 경우가 어떤 것인지 설명 부탁드려도 될까요?

앗 값이 없는건 null로 들어오나요? 값이 없으면 dto.request의 필드에 설정된 기본값으로 처리되는 줄 알고 구현했네요.

예를 들어 입력값이 비어 있으면 익셉션을 출력하라는 부분에서 if(변수==null)이 아니라 if(변수==기본값)으로 썼는데도 익셉션이 제대로 나왔던 것 같은데, 다시 한 번 확인해보겠습니다..!

Jhvictor4 commented 3 years ago

@yg-moon 바디에 key가 비어있으면 기본적으로 null이고, nullable 처리 후 기본값 부여하면 기본값으로 들어옵니다. example: String? = '기본값' << 여기서 물음표 지우면 key를 비울 수 없습니다.

@WallabyG @yg-moon 1번의 경우 var 설정 후 값 변경하면 JPA가 알아서 영속성 context 인지하고 덮어씁니다. var 쓰기 싫은 경우 엔티티를 data class 선언하시고 .copy() 메소드 통해서 변경하고자 하는 값만 넘겨주신 뒤, 그렇게 생성한 새 엔티티(업데이트된 놈) save해주시면 됩니다.

Jhvictor4 commented 3 years ago

newEntity = originalEntity.copy(changingAttribute=NewAttribute)

이런식

2번은 에러코드 정해놓고 코드당 하나만 만드는게 좋긴 합니다.

4번은 nullable은 필요한 곳에만 사용하는 것이 좋습니다.

6번은 제가 잘 모를 수도 있는데, @Transactional 어노테이션은 더티체킹 보다는 exception 발생 시 롤백 여부를 정해주는 annotation입니다. 한 호출에서 엔티티를 여러개 만들 경우, Transactional 어노테이션이 없으면 도중에 에러가 났을 때 DB가 꼬일 수 있습니다.

pullup-dip-burpee commented 3 years ago

@Jhvictor4 님 6번에 대한 답변에 덧붙이자면, 구글링해서 나오는 블로그들에서는

  1. @Transactional 어노테이션을 붙이거나
  2. 명시적으로 EntityTransaction 을 통해 트랜잭션 범위를 지정하는 경우

트랜잭션으로 취급되어서 dirty checking이 작동한다고 합니다. @Transactional 어노테이션에 아마 트랜잭션임을 지정해주는 기능이 있나봅니다.

출처 링크

Hank-Choi commented 3 years ago

안녕하세요 답이 매우 늦어서 죄송합니다. 다른 분들이 많이 답변 달아주셔서 간단히 달겠습니다.

  1. val로 두고 setter를 통해 변경하는 것은 불가능합니다. val 자체가 불변을 나타내기 때문에 var로 변경하신 부분은 잘 하셨습니다!

  2. 에러가 발생하는 것이 별개의 상황이라면 exception, error type 전부 세분화 하는 것이 좋을 듯 합니다. 언제 각각의 상황에 따라 다른 작업을 처리해야 할 지 모르니깐요. 다만 계층을 나누어 비슷한 exception끼리는 부모 클래스로 묶으면 더욱 좋을 것 같습니다.

  3. 위에 답이 있는 듯 하여 패스하겠습니다.

  4. 제 스켈레톤 코드를 보면 아시겠지만 null이 들어가는 것을 웬만하면 피했습니다. nullable type은 필요한 경우 외에 최대한 피하는 것이 좋다고 생각하시면 될 듯 합니다. 예시의 경우에는 nullable 처리하는 것이 합리적인 듯 하지만 null이 아닌 것이 확실한 경우 (?. !!을 써야 하는 경우) 인자로 받아올 때 최대한 not-nullable type을 사용해 배제하는 식으로 받는 것이 좋을 듯 합니다.

  5. 이 질문 또한 다른 분들이 좋은 답변을 해주셔서 스킵하겠습니다.

  6. 세미나 2 다시보기 하시면 나올 것 같습니다. db transaction에 대해 설명했고 한 api call에서 db 데이터 삽입/수정 쿼리 여러개를 날리는 경우 꼭 사용하시는 것이 좋습니다.

과제4에서 테스트 코드 작성 및 수정 과제가 나갈 예정이라 말씀하신대로 과제4 이후 모범답안을 올리겠습니다!

yg-moon commented 3 years ago

답변 주신 모든 분들께 다시 한 번 감사드리며 이슈는 닫도록 하겠습니다!