[Spring] 입문_개인과제

조성현·2022년 12월 7일
0

Spring 입문 주차 개인 과제

Goal: "스프링 부트로 로그인 기능이 없는 나만의 블로그 백엔드 서버 만들기"

notification : 과제 주의사항

1. Entity를 그대로 반환하지 말고, DTO에 담아서 반환해주세요!
2. 프론트엔드와 백엔드가 느슨하게 결합하는 환경이 ”최근에는” 더 일반적이라고 말씀드렸죠? 앞으로 남은 강의 예제와 실습에서는 html/css/js 즉 뷰도 같이 반환 하겠지만, 과제에는 여러분들이 서버 로직에 더 집중하실 수 있도록 JSON을 반환하는 API형태로 진행하려고 합니다.
3. 눈으로 직접 확인 할 수 있었던 view와는 다르게, 여러분들이 과제를 진행하시려면 서버가 반환하는 결과값을 더 쉽게 확인 하실 수 있는 도구가 필요합니다. 바로 PostMan 입니다. 사용법은 아래 페이지에서 꼭 확인해주세요!

서비스 완성 요구사항

1. 아래의 요구사항을 기반으로 Use Case 그려보기
- 손으로 그려도 됩니다.
- [Use Case 설명글](https://narup.tistory.com/70) 

2. 전체 게시글 목록 조회 API
- 제목, 작성자명, 작성 내용, 작성 날짜를 조회하기
- 작성 날짜 기준 내림차순으로 정렬하기

3. 게시글 작성 API
- 제목, 작성자명, 비밀번호, 작성 내용을 저장하고
- 저장된 게시글을 Client 로 반환하기

4. 선택한 게시글 조회 API
- 선택한 게시글의 제목, 작성자명, 작성 날짜, 작성 내용을 조회하기 
(검색 기능이 아닙니다. 간단한 게시글 조회만 구현해주세요.)

5. 선택한 게시글 수정 API
- 수정을 요청할 때 수정할 데이터와 비밀번호를 같이 보내서 서버에서 비밀번호 일치 여부를 확인 한 후
- 제목, 작성자명, 작성 내용을 수정하고 수정된 게시글을 Client 로 반환하기

6. 선택한 게시글 삭제 API
- 삭제를 요청할 때 비밀번호를 같이 보내서 서버에서 비밀번호 일치 여부를 확인 한 후
- 선택한 게시글을 삭제하고 Client 로 성공했다는 표시 반환하기

과제 풀이 1단계: 설계를 먼저 하자

명확히 개념들을 이해한 상태에서 설계를 진행한 것이 아니다보니 부족한 부분들이 많습니다.
(예쁘게 봐주세요.. 꾸벅)

[Use Case Diagram을 그려보았다.]

[API 명세서를 작성해보았다.]

[양식을 작성해보았다.(사실 이제와서 생각해보면 이 내용들이 API 명세서에 담겨야 할 내용들이다.) ]


과제 풀이 2단계: 입문 강의에서 클론코딩했던 코드를 확장시켜 과제를 완성하자.

스프링 입문강의 메모서비스 레파지토리 링크(클론코딩)
나만의 블로그 백엔드 서버 만들기 레파지토리 링크(이번 과제 결과물)

프로젝트 분석(을 가장한 배운점 정리)

1. 모든 Request와 Resonse는 Dto로 '포장'되어 이동한다.

  • '왜?' Entity가 아닌 Dto로 포장해야 하는지 이유가 궁금했다.
    이유를 알아야 목적에 맞게 잘 사용할 수 있다 -> [ how 이전에 why부터 이해하자 ]

  • 그래서 WHY를 찾아보았다 -> 요청과 응답으로 Entity 대신 Dto를 사용하자 - Tecoble

  • 만약 API별로 요청과 응답에 담길 데이터가 달랐다면 Dto가 많~~은게 맞지만, 이번에는 처음이라 그런지 API들이 같은 형식으로 요청, 응답이 이루어졌기에 각각 하나씩만 만들어주었다.

2. JpaAuditing을 활용하여 객체 생성, 수정시간을 기록하도록 하였다.

  • 클론코딩을 하며 어렴풋이 이런 원리로 됐겠거니~ 싶었던 기능이었다.
  • 어떤 원리로 작동하는 지를 먼저 파악하고 과제에 반영하였다.
    (JPA Auditing 기능을 사용해서 생성, 수정 일자 자동화하기)
  • Application 에 @EnableJpaAuditing을 붙여야 작동한다는 사실을 모르고, 왜 작동하지 않는지 고민하며 정수리가 뜨거워져가는 동기들에게 도움을 주었다.

3. 비밀번호 검증 로직에서 배운 것들

// service.PostService (수정전)
    public boolean pwIsValid(String inputPw, String postPw){
        return inputPw.equals(postPw);
    }
    
    @Transactional
    public String deletePost(Long id, String pw) {
        Post post = postRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("게시물이 존재하지 않습니다.")
        );
        if(pwIsValid(pw, post.getPassword())){
            postRepository.deleteById(id);
            return "삭제에 성공했습니다.";
        } return "비밀번호가 다릅니다. 삭제 실패";

기존에 작성했던 비밀번호 검증 로직(위의 코드)은 boolean 값을 반환해주는 방식이었다.
이 비밀번호 검증 로직의 문제점

1. 비밀번호 검증을 Service가 하고 있다.
- 비밀번호를 가지고 있는 것은 Post 객체이다.
- 값을 가지고 있다는 것은 그것에 대한 '역할과 책임'을 가진다는 것이다 -> Post 객체에서 비밀번호 검증을 해야한다.

2. pwIsValid 메서드 자체도 본인의 역할을 다 하지 못하고 있다.
- 위 코드를 보면 pwIsValid 메서드가 boolean 값을 return해주기 때문에 받는 쪽에서 if로 추가적인 작업을 해줘야한다.
- 비밀번호 검증 로직의 역할은 **'비밀번호가 맞다면 다음 로직이 실행되도록 하고, 틀릴 경우에는 다음 로직이 실행되지 못하게 하는 것이다.'

수정한 코드

/// entity.Post (수정 후)
    public void validatePassword(String inputPassword) {
        if (!inputPassword.equals(this.getPassword())) throw new IllegalArgumentException("비밀번호 불일치");
    }
/// service.PostService (수정 후)
    @Transactional
    public void  deletePost(Long id, String inputPassword) {
        Post post = postRepository.findById(id).orElseThrow(PostNotExistException::new);
        post.validatePassword(pw);
        postRepository.deleteById(id);
  • 비밀번호를 가지고 있는 Post 객체에서 비밀번호 검증을 하고있다.
  • return을 주는 것이 아니라 비밀번호 불일치시에 IllegalArgumentException를 던져주는 방식으로 비밀번호가 일치하면 '아무것도 하지 않는'방식으로 코드를 작성했다.

4. 아무것도 반환하지 않는 것도 응답이다.

다시 한번 위의 코드(수정 전)를 살펴보자
비밀번호 검증메서드에서도 return / deletePost 메서드에서도 return 아주 return 범벅이었다.

  • 요구사항에서 '반환하라'라는 의미를 return으로 받아들인 탓이 컸다.
  • 튜터님의 코드리뷰 과정에서 '아무것도 반환하지 않는 것도 응답이다.'라는 배움을 얻고 (수정후) 코드로 발전할 수 있었다.

5. 나도 이제 stream 쓸 수 있다!

    @Transactional(readOnly = true)
    public List<PostResponseDto> getAllPosts(){
        return postRepository.findAllByOrderByModifiedAtDesc().stream()
                .map(PostResponseDto::new).collect(Collectors.toList());
    }
  • List<Post>List<PostResopnseDto>로 만드는 과정을 stream으로 구현했다. 심지어 자세히 살펴보면 method reference도 하나 들어가있다.
  • 조금씩 늘어가는 맛에 공부한 보람을 느낀다..!

Postman을 처음으로 사용해보았다.

  • 프론트엔드가 구현되어있지 않아도 내 코드가 정상적으로 작동되는지, 예외 발생시 어떻게 메세지가 전송되는지 등을 확인할 수 있었다.
  • 더 편한 점은, 해당 요청들을 컬렉션으로 만들어서 한번에 테스트할 수 있다는 점이었다.
  • [포스트맨 개요/ 설치/ 사용법]

이어지는 글에서는 이번 개인과제 기간에 접하게 된 다양한 글들을 다시 한번 읽어보며 정리해보고, 약간의 회고도 곁들이려한다. (2부 글 링크)

profile
맛있는 음식과 여행을 좋아하는 당당한 뚱땡이

0개의 댓글