늦은 프로그래밍 이야기

221223 TIL (개인과제 리팩토링) 본문

내일배움캠프/TIL, WIL

221223 TIL (개인과제 리팩토링)

한정규 2022. 12. 23. 22:18

개인과제 리팩토링

스프링 숙련강의 개인과제를 리팩토링 해보았다. 오늘 모두 끝마치고 싶었지만, 특강도 있었고 크리스마스 기념해서 학원에서 연말 이벤트도 있고 해서 끝마치지 못했다. 주말새에 리팩토링을 모두 마무리 하고 다음주부터는 심화강의를 시작해야 할 것이다.

 

RestAPI

BlogController의 RestAPI url 수정

기존의 API url을 보다 Restful하게 수정하였다. postings → posts로 변경. (url에는 명사를 사용해야 하는데 동사사용 이슈) comment/{id} → posts/{postId}/comments로 변경.(보다 명확하고 Restful하게 변경)

user/signup, user/login → /signup, /login으로 변경. (signup과 login만으로 기능을 표현할 수 있으므로 user 삭제)

 

getAllPosting 메소드와 getPostingByUsername 메소드 결합

@GetMapping("/posts")
publicList<PostingResponseDto> getAllPostingsOrGetPostingByUsername(@RequestParam(required = false) String username) {
    if (username == null) {
        return blogService.getAllPostings();
    } else {
        return blogService.getPostingByUsername(username);
    }
}

과제 요청사항에는 없었지만 임의로 username으로 조회하는 기능을 추가했는데 GET방식 요청이 3개나 되어서 어떻게 할까 고민하던 중 과제 리뷰에서 모든 posting을 조회하는 것과 username으로 조회하는 것 둘 다 Posting의 List를 조회하는 것이기 때문에 둘을 합칠 수 있을 거라는 피드백을 받고 @RequestParam (required=false)를 사용하여 쿼리 파라미터에 반드시 값이 안 들어와도 되게 설정을 한 뒤 username값이 null이면 모든 Posting을 조회하고 값이 들어있으면 username으로 조회하는 기능으로 둘을 합쳤다.

 

@Requestparam 대신 @Pathvariable을 사용해보려 했지만 모든 Posting 조회를 하려고 아무것도 입력을 안했을 때에 동작을 하지 않았다. 그래서 Pathvariable의 required 사용 가능성을 질문하러 갔다가 username 같은 String 정보를 url에 노출하는 것은 검색 결과 같은 것에 전략적으로 사용해야 한다는 말을 들었다. url에 노출하지 말아야 할 것들은 RequestBody로 요청을 받아야 하고 String 정보를 url에 노출하는 것은 가급적 지양해야 할 것이다.

@GetMapping("/posts")
publicList<PostingResponseDto> getAllPostingsOrGetPostingByUsername(@RequestBody(required = false) String username) {
    if (username == null) {
        return blogService.getAllPostings();
    } else {
        return blogService.getPostingByUsername(username);
    }
}

보편적으로 @RequestBody를 사용한다고 하여 RequestParam을 RequestBody로 바꿔주었다. 하지만 아무것도 값을 안넣어줬을 때 모든 Posting 조회 기능은 동작하지만, username을 body에 넣어줬을 때 아무것도 검색이 되지 않는다.

 

디버깅을 해보았는데 body에 JSON 형식으로 username을 입력했는데 {”username” : “aaaa”} 중 aaaa만 입력이 되는 것이아니라 {”username” : “aaaa”} 전체의 값을 username으로 받아 조회가 되지 않는 것을 알 수 있었다. 그래서 aaaa만 입력해 보았더니 제대로 조회되는 것을 확인하고, 다른 RequestBody의 값들을 확인했더니 RequestDto로 받고 있었다.

 

UsernameRequestDto 클래스를 작성한 후 RequestBody로 UsernameRequestDto를 받았더니 정상적으로 작동했다. 객체 타입으로 값을 받아야 JSON 형식이 변환되는 것을 확인하였고, JSON → 객체, 객체 → JSON으로 Jackson이 자동으로 변환해 주는 것을 알게 되었다.


테이블 관계

Comment를 생성할 때에 Posting의 commentList에 add하는 기능 삭제

POSTING CLASS

public void putCommentOnPosting(Comment comment) {
    this.commentList.add(comment);
}

COMMENTSERVICE CLASS

posting.putCommentOnPosting(comment);

Java의 관점에서 Comment를 Posting entity의 commentList 필드에 넣어줘야 한다고 생각을 해서 putCommentOnPosting이라는 메소드를 만들어서 Comment를 생성할 때마다 넣어줬지만, 다른 수강생의 코드를 보았을 때 넣어주지 않아도 Posting을 조회하였을 때 Comment가 같이 조회 되는 것을 보고 곰곰히 생각해 보았더니 mappedBy로 연관관계를 설정해 주어서 Posting을 조회할 때 JPA가 join 쿼리를 날려서 comment 테이블의 값들을 불러와 주는데 직접 또 넣어주는 것은 불필요한 기능이라 느껴 삭제함.


순환참조

순환참조가 발생하여 사용했던 @JsonManagedReference, @JsonBackReference 대신 Dto를 사용하여 순환참조 문제를 해결

양방향 연관관계에서의 순환참조 문제는 직렬화, 역직렬화 과정에서 Jackson이 Getter를 사용하여 발생한다고 한다. Posting과 Comment를 연관관계를 맺었는데, Posting을 조회하면서 그 Posting에 달려 있는 댓글 (CommentList)를 같이 조회하는 과정에서, Posting은 CommentList를 참조하고 Comment는 Posting을 참조하기 때문에 서로를 반복하여 참조하며 순환참조 문제가 발생하였다.

 

과제를 진행할 때는 Exception과 StackOverflowError가 발생하여, 검색하다 안 나오고 해결할 방법을 몰라 튜터님들과도 상의하고, 주변 수강생들과도 상의하며 많은 시간을 소요한 결과, 순환참조일 수 있다는 말을 들어 순환참조에 관해 검색해보던 결과, @JsonManagedReference와 @JsonBackReference를 Posting과 Comment에 각각 작성해주면 해결된다는 것을 보고 입력하였더니 해결되었는데, 이것은 임시방편이고 근본적인 문제를 해결해야 한다는 과제리뷰 강의에 따라 DTO를 사용하여 Comment의 Posting은 DTO에서 제외하여 순환참조 문제를 해결하였다.

public PostingResponseDto(Posting posting) {
    this.createdAt = posting.getCreatedAt();
    this.modifiedAt = posting.getModifiedAt();
    this.id = posting.getId();
    this.title = posting.getTitle();
    this.username = posting.getUser().getUsername();
    this.contents = posting.getContents();
		List<CommentResponseDto> commentResponseDtoList = new ArrayList<>();
    for (Comment comment : posting.getCommentList()) {
        commentResponseDtoList.add(new CommentResponseDto(comment));
    }
    this.commentList = commentResponseDtoList;
}

DTO의 중요성을 다시 한번 깨달았고, DTO가 그저 유지보수의 편리함만을 제공하는 것이 아니라 순환참조의 문제를 해결할 수도 있다는 것을 알게 되었고, DTO를 적극 사용해야겠다는 생각이 들었다.


TIL 특강

TIL 특강을 들었는데 그동안 TIL을 잘못 쓰고 있었다는 것을 느꼈다. 블로그를 그저 이쁘게 정리하기 보다는 핵심적으로 발생한 문제, 시도해 본 것들, 해결한 내용, 알게된 점 등을 적어야 하는데 그동안 그렇게 하지 못한 것 같다. 그래서 오늘은 그저 내용을 정리한 글보다는 리팩토링에 대한 기존 코드의 문제점, 시도해 본 내용, 해결한 것, 알게된 점 등을 리팩토링 작업을 하며 메모해 둔 후 작성을 해보았다.

 

이제부터라도 알았으니 다행이라 생각하고 앞으로는 그렇게 작성하도록 노력하도록 할 것이다. 그저 이쁘게 정리된 글이 아닌 투박하더라도 나만의 아이덴티티가 녹아있는, 나만 쓸 수 있는 TIL을 쓰도록 해야겠다.


'내일배움캠프 > TIL, WIL' 카테고리의 다른 글

221226 TIL (FetchType)  (0) 2022.12.26
8주차 WIL  (0) 2022.12.25
221222 TIL  (0) 2022.12.23
221221 TIL  (0) 2022.12.22
221220 TIL (@OrderBy)  (0) 2022.12.21
Comments