240103 TIL | step1피드백, step2정리, step3

2024. 1. 3. 21:07· 왕초보일지

 

STEP 1 피드백

 read.me 구체적으로 작성, commit message 의미 있는 코멘트, '클린코드', 강의 클론 코딩, dirty checking

=>

- read.me : 줄이 자꾸 이상해져서 헤맸는데 마크다운문법이란게 따로 있단다...3, 4단계까지 다 하고 난뒤에 정리해야겠다.

- commit 코멘트 : 기능단위별로 푸쉬하고싶은데 자꾸 이것저것 눈에 보이는걸 건들다보니까 잘 안된다.

- step2 클린코드 : 챌린지 반 2주차 세션 라이브 코딩을 보면서 뭔가 더 알아보기 쉽게 작성하고 싶다는 마음이 들었는데 어떻게 손을 대야할지 모르겠다.

 

@Transactional
    override fun updateCard(cardId: Long, request: UpdateCardRequest): CardResponse {
        val card = cardRepository.findByIdOrNull(cardId) ?: throw ModelNotFoundException("Card", cardId)
        when(request.status) {
            CardStatus.TRUE.name -> card.isCompleted()
            CardStatus.FALSE.name -> card.isNotCompleted()
        }
        card.title = request.title
        card.content = request.content
        card.name = request.name
        return card.toResponse() // update 시 영속성 컨텍스트에 대해 dirty checking 일어나므로 save 할 필요 X
        }

서비스단에서 할일의 상태변경을 아래와 같이 card entity의 메소드로 줄였다.

@Service
class CardServiceImpl( ... ) {
...
@Transactional
    override fun updateCard(cardId: Long, request: UpdateCardRequest): CardResponse {
        val card = cardRepository.findByIdOrNull(cardId) ?: throw ModelNotFoundException("Card", cardId)
        card.isCompletedOrNot(request, card)
        card.title = request.title
        card.content = request.content
        card.name = request.name
        return card.toResponse() // update 시 영속성 컨텍스트에 대해 dirty checking 일어나므로 save 할 필요 X
        }
 ...}
 
 @Entity
 calss Card( ... ) {
 ...
 fun isCompletedOrNot(request: UpdateCardRequest) {
        when(request.status) {
            CardStatus.TRUE.name ->  status = CardStatus.TRUE
            CardStatus.FALSE.name -> status = CardStatus.FALSE
        }
    }
 ...
 }

 

 

비밀번호 검사 메소드를 만들어서 댓글의 수정, 삭제 서비스단에 반영했다.

@Service
class CardServiceImpl( ... ) {
...
@Transactional
    override fun updateComment(cardId: Long, commentId: Long, request: UpdateCommentRequest): CommentResponse {
        val card = cardRepository.findByIdOrNull(cardId) ?: throw ModelNotFoundException("Card", cardId)
        val comment = commentRepository.findByIdOrNull(commentId) ?: throw ModelNotFoundException("Comment", commentId)

        // 비밀번호 검사
        if(comment.isNameAndPasswordInCorrect(request)) throw UnauthorizedAccess()

        comment.content = request.content
        return comment.toResponse()

    }
...}

@Entity
class Comment( ... ) {
...
fun isNameAndPasswordInCorrect(request: CheckRequest): Boolean {
        return request.name != this.name || request.password != this.password
    }
...}

서비스단에서 기능이 명확하게 보이게 하기 위한 메소드를 생각하다보니 그럼 그렇게 되면 Entity에 많은 메소드들이 몰려있어도 되는걸까하는 고민이 들었다.

할일 생성과정에서 request의 값을 저장하는 것도 메소드로 만들 수 있고

업데이트할 때 값을 할당하는 것도 메소드로 만들 수 있고 얼마든지 바꿀 수 있는데 더 나은 코드는 뭘까 .!.!

 

- 클론 : 개인과제 다 끝낸 후 한 번 더 해보기

- dirty checking : 영속성 컨텍스트, update 시 save 함수 불필요


STEP2 QnA

  • 처음 설계한 API 명세서에 변경사항이 있었나요? 변경 되었다면 어떤 점 때문 일까요? 첫 설계의 중요성에 대해 작성해 주세요!
  • ERD 와 DTO 를 수정했다. 컬럼 변경사항이 있었기 때문.
  • 첫 설계를 통해 내가 개발하고자 하는 어플리케이션의 전체적인 흐름을 파악할 수 있다.

   2. ERD를 먼저 설계한 후 Entity를 개발했을 때 어떤 점이 도움이 되셨나요?

  • 내가 지금 작성하고 있는 Entity 와 다른 Entity 와의 관계를 인지하며 작성할 수 있다.

   3. 만약 댓글이 여러개 달려있는 할일을 삭제하려고 한다면 무슨 문제가 발생할까요? Database 테이블 관점에서 해결방법이 무엇일까요? 

  • 테스트 해봤을 때 응답도 성공적으로 되고 DB내부에서도 할일과 댓글이 잘 삭제되었다.
  • 할일과 댓글의 관계설정에서 cascade 를 통해 영속성을 전파하고 orphanRemoval 을 통해 부모와의 연결이 끊긴 댓글을 삭제하도록 설정했기 때문인 것 같다.
   @OneToMany(mappedBy = "card", fetch = FetchType.LAZY, cascade = [CascadeType.ALL], orphanRemoval = true)
    val comments: MutableList<Comment> = mutableListOf()

 

   4. IoC / DI 에 대해 간략하게 설명해 주세요!

IoC : 객체가 제어의 권한을 다른 곳에 위임한다. spring에서는 빈으로 등록된 객체들을 IoC 컨테이너가 관리한다.

DI : 의존성을 주입하는데 자체적으로 생성하는 것이 아니라 외부에서 주입하므로써 객체 간 결합도를 낮춘다. 


STEP 3

1. 할 일 목록에서 작성일 기준 오름차순, 내림차순 정렬 기능 추가
api를 요청할 때 정렬 기준(오름차순, 내림차순)을 포함하기
정렬 기준을 통해 정렬한 할 일 목록 반환하기
2. 할 일 목록에 작성자 기준 필터 기능 추가
api를 요청할 때 작성자 이름을 포함하기
작성자 이름이 일치하는 할 일 목록 반환하기

 

목록을 불러올 때 정렬기준과 작성자이름을 request로 받고 서비스단의 목록 조회 로직을 수정한다.

이때 쿼리메소드를 활용해야하는건가?

기본값은 어떻게 설정해놓는거지? => name 의 경우 일치하지 않으면 조회가 안되야 올바른 서비스다 !

override fun getAllCards(request: GetCardListRequest): List<CardResponse> {
       var response: List<CardResponse> = listOf()
            if(request.orderBy == CardOrderBy.DESC.name) {
            response = cardRepository.findAllByName(request.name).sortedByDescending { it.createdAt }
        } else {
            response = cardRepository.findAllByName(request.name).sortedBy { it.createdAt }
        }
        return response
    }

 

쿼리메소드로 이름반환은 하겠는데 request로 받아온 정렬은 잘 모르겠다. 그래서 if 문으로 돌리니 이렇게 지저분한데 어떻게 고쳐야 할지 모르겠다....

CardResponse의 status 가 set이 안된다는 오류가 떴었다.

 => enum 클래스인 status의 타입을 클래스로 선언

data class CardResponse(
    val id: Long,
    val status: CardStatus,
    val title: String,
    val content: String?,
    val createdAt: String,
    val name: String
)

 

 

 

*기존 단건 카드 조회 시

 override fun getCard(cardId: Long): CardWithCommentResponse {
        val card = cardRepository.findByIdOrNull(cardId) ?: throw ModelNotFoundException("Card", cardId)
        val comments = commentRepository.findByCardId(cardId).map { it.toResponse() }
        return CardWithCommentResponse(card.toResponse(), comments)
    }

card 따로 comment 따로 불러왔었는데 이 둘은 관계설정을 해놨기 때문에 card.comment 로 불러올 수 있다고...한다...😰

fetchtype 도 그냥 아~나중에 불러오는 거구나 했는데 이런 때에 쓰일 수 있다고 한다. 

 override fun getCard(cardId: Long): CardWithCommentResponse {
        val card = cardRepository.findByIdOrNull(cardId) ?: throw ModelNotFoundException("Card", cardId)
        return CardWithCommentResponse(card.toResponse(), card.comments.map { it.toResponse() })
    }

CardResponse 를 상속받아서 댓글이랑 같이 반환하는 형태로 어떻게 만들 수 있는지 나아아아중에 고민해보기

 

 

 

3. 할 일 작성, 수정시 validation 추가
 - 제목 1자 이상 200자 이내 검사
 - 본문 1자 이상 1000자 이하 검사
 - 조건 불충족 시 기능 실패 응답

 

Spring boot Validation

참조

 

복잡한 검증 로직을 어노테이션으로 해결할 수 있다.

 

의존성

implementation ("org.springframework.boot:spring-boot-starter-validation")

 

 

Controller 의 @RequestBody 에 @Validated 선언

해당 DTO 에 제약조건 @field: 로 명시

class CardController(...) {
...
@PostMapping
    fun createCard(@Validated @RequestBody createCardRequest: CreateCardRequest): ResponseEntity<CardResponse> {
        return ResponseEntity.status(HttpStatus.CREATED).body(cardService.createCard(createCardRequest))
    }
...}

data class CreateCardRequest(
    @field:Size(min = 1, max = 200, message = "제목은 1자 이상 200자 이하여야 합니다.")
    val title: String,
)

 

@RequestParam 과 @Pathvariable 은 Controller 에 @Validated 선언하고 제약조건 추가

 

파리미터가 유효하지 않을 때

@RequestBody   MethodArgumentNotValidException 에러

@RequestParam/@PathVariable  ConstraintViolationException 에러

 

제목과 내용을 공백으로 제출해봤다.

 

기능실패 400 코드와 메시지가 잘 반환되는데 원래 이렇게 기나?! 이런 경우 친절한 에러메시지를 작성하기 위해 커스텀한다고 하는데 정확히 어떤 형식으로 하는지 잘 모르겠다.

 

 

 


Spring Data JPA

getById

@Override
public T getById(ID id) {
    Assert.notNull(id, ID_MUST_NOT_BE_NULL);
    return em.getReference(getDomainClass(), id);
}

LAZY 형태로 값을 가져온다. == 일단 proxy로 만든 다음 실제 사용할 때 DB에 접근한다.

=> 없는 데이터를 조회하면 실제로 접근할 때 EntityNotFoundException 에러가 발생한다.

 

 

findById

실제로 DB에서 값을 찾아오기 때문에 객체가 없다면 Null 을 반환한다.


STEP4

1. 할 일 카드 목록 api의 응답에, 연관된 댓글 내용을 추가해주세요.
할 일 목록과 댓글 목록을 효율적으로 가져와서 매칭하려면 어떻게 해야 할까요?
N + 1 query 문제 알아보기
2. 할 일 카드 목록 api에 pagination 기능을 추가해주세요.
사용자가 많아져서 할 일 카드가 너무 많아지면 어떤 일이 벌어질까요?
offset 기반 pagination과 cursor 기반 pagination에 대해 알아보기
3. 회원가입, 로그인 기능을 추가해주세요.
로그인 한 사용자가 자신의 할 일, 댓글만 수정, 삭제할 수 있게 해주세요.
인증, 인가에 대해 알아보기: 요청한 사용자가 누구인지, api를 호출할 권한이 있는지를 어떻게 알 수 있을까요? basic authentication과 bearer authentication에 대해 알아보기
basic auth에 비해 token 기반 auth가 가지는 장점이 무엇일까요?

목록에 댓글도 같이 가져오려면 오늘 만든거 결국은 수정해야 하는건가 😰😰😰

CardWithCommentResponse를 List로 만들어주면 되려나

 

'왕초보일지' 카테고리의 다른 글

240105 TIL | Todo과제  (1) 2024.01.05
240104 TIL |  (2) 2024.01.04
240102 TIL |  (0) 2024.01.02
231230 | TIL  (0) 2023.12.30
231229 TIL | 스프링 부트 3 기본 지식  (2) 2023.12.29
'왕초보일지' 카테고리의 다른 글
  • 240105 TIL | Todo과제
  • 240104 TIL |
  • 240102 TIL |
  • 231230 | TIL
다시은
다시은
🔥
다시은
재은로그
다시은
전체
오늘
어제
  • 분류 전체보기 (127)
    • 코딩테스트 (40)
    • Language (2)
      • JAVA (2)
      • Kotlin (0)
      • TypeScript (0)
    • SQL (1)
    • 인프라 (1)
    • 왕초보일지 (77)
    • 회고 (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • googleapis
  • mysql
  • sql
  • 문자열변환
  • SQL문법
  • 스프레드시트
  • Kotlin

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
다시은
240103 TIL | step1피드백, step2정리, step3
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.