[01.06] 내일배움캠프[Spring] TIL-48

박상훈·2023년 1월 6일
0

내일배움캠프[TIL]

목록 보기
48/72

[01.06] 내일배움캠프[Spring] TIL-48

👋🏼Intro.


👪팀 소개

https://github.com/PriceHoon/groupSix

완성보다 하나라도 배우는 것이 목표인 2*3 ==6 조!

  • 팀원 소개/역할
김민수 - 댓글 좋아요 기능 구현 / 시큐리티 

박상훈 - 게시글 좋아요 / 시큐리티 

손혜은 - Comment CRUD 구현

이재원 - User CRUD 구현

장현재 - Board CRUD 구현
  • 우리의 약속
    • 09:00 : 간단히 진행사항과 당일 목표/계획 공유

       15:00 : 목표치 달성도 & 문제점 공유
    • 개인이 맡은 기능에 대해 충분한 고민과 검색 후 안 되면 팀에 문제점 공유하기((max 1H)

      → 정해진 시간이 아니더라도 해결이 안 되는 문제는 바로 공유!!

    • 깃 커밋시 알려주기

프로젝트 소개

  • user - 회원가입, 로그인 기능
  • 스프링 시큐리티를 사용하여 인증된 회원만 인가된 기능 사용할 수 있도록 변경
    • 게시글 & 댓글 - 작성(C) / 조회(R) / 수정(U) / 삭제(D)
    • 게시글 & 댓글 좋아요 생성 / 삭제 / 조회 기능
  • 해당 기능들이 존재하는 페이지API 만들기

⚙️진행과정


기획

숙련2구현

시큐리티 적용

발표준비&발표

  • 12/30(금)
    • 기획
    • ERD 데이터 베이스 설계
    • UML 그리기
    • API명세 작성
    • 역할 분담
  • 01/02(월)
    • 숙련 과제2 작업 시작
      • User / Board / Comment 기본 CRUD API 설계
      • LikeBoard/Comment의 방식 의견 교환
    • 깃 레파지토리 생성
  • 01/03(화)
    • 숙련 과제2 작업
      • User / Board / Comment 기본 CRUD API에 JWT를 사용할 수 있도록 추가

      • Like 부분에 대한 작성 및 기능간의 테스트 실행

  • 01/04(수)
    • 숙련 과제2 마무리
      • 숙련 과제2까지의 과정 완료
    • 시큐리티 시작
      • 시큐리티 적용을 위해 해당 개념 학습

      • 프로젝트 내에 시큐리티 적용

  • 01/05(목)
    • 시큐리티 마무리
      • 시큐리티 동작 구조 파악하기
    • 코드 리펙토링
      • 시큐리티 적용으로 인한 서비스 코드에서 회원검증 로직 삭제
        • 서비스는 인가된 회원만 접근하는 방향으로 로직 변경
      • 기능 사용을 위해 해당 회원이 자격이 있는지 파악하는 로직 위치 변경
        • service → 각 entity 부분 (User , Board, Comment)
      • REST한 API를 위해 반환 상태 코드의 변경
        • 성공(200) / 실패(400) → 경우에 맞춰서 생성(201) / 삭제(204) / 발견x(404) ..ETC
    • 발표 준비
  • 01/06(금)
    • 발표 준비 & 발표

ERD 데이터 베이스 설계

UML

API 명세

API

🎬영상 링크


https://youtu.be/MBRbaZyPPj0

⛳️트러블 슈팅


이재원

  • Sourcetree 사용하는데 익숙해졌다고 생각했는데 브랜치 병합과정에서 예상하지 못한 여러가지 문제가 발생해서 로컬 저장소를 삭제하는 경우가 있었음.
  • 유효성검사-정규표현식
    이전 프로젝트에서 정규표현식을 작성해 본 적이 있었는데 이번 프로젝트에서는 @Pattern, @Size, @Valid를 사용해서 코드를 짜야했다. 프로젝트 초반에 스프링 숙련, 심화 과정에 대한 이해가 부족해서 구현에 어려움을 많이 느꼈음.

김민수

  • Test code
  • 상황
    • 맡은 바 기능을 구현 후 해당 기능을 테스트 해보고 싶지만 아직 코드의 통합이 되기 전이기에 해당 기능을 테스트할 방도가 없었음.
    • 테스트에 성공 및 기능이 성공하면 코드를 합쳐야 하는데 테스트할 방도가 한정됨
  • 선택사항
    • 1 : 이전에 구현한 프로젝트로 해당 기능을 가져가서 postman으로 테스트 하기
    • 2 : Test 코드를 작성, 해당 기능의 도출 값이 원하는대로 잘 돌아가는지 확인하기
  • 선택 - 2
    • 이전의 프로젝트로 가져가서 기능을 테스트 하는 것 보단 스프링 내에서 해당 코드를 테스트 하기로 결정
    • 테스트 코드 학습 후 해당 기능에 대한 테스트 코드 작성
    • 테스트의 동작 및 기능 성공 확인 후 해당 코드 병합
  • 코드
    		@Mock
        LikeCommentRepository likeCommentRepository;
        @Mock
        CommentRepository commentRepository;
        @Mock
        UserRepository userRepository;
    
        @InjectMocks  // 가짜 객체 삽입,
        LikeCommentService likeCommentService;
    
        @Test
        @DisplayName("좋아요 성공 케이스")
        void clickFavorite() throws Exception {
    
            //given
            User user = mock(User.class);
            Comment comment = mock(Comment.class);
            when(user.getUsername()).thenReturn("userA");
            when(comment.getId()).thenReturn(1L);
            when(userRepository.findByUsername(user.getUsername())).thenReturn(Optional.of(user));
            when(commentRepository.findById(comment.getId())).thenReturn(Optional.of(comment));
            when(likeCommentRepository.existsByUserAndComment(user, comment)).thenReturn(false);
    
            //when
            String s = likeCommentService.clickFavorite(comment.getId(), user.getUsername());
    
            //then
            Assertions.assertSame("좋아요를 누르셨습니다",s);
        }
    
        @DisplayName("좋아요 실패 케이스")
        @Test
        void clickFavoriteError() throws Exception {
            //given
            User user = mock(User.class);
            Comment comment = mock(Comment.class);
            when(user.getUsername()).thenReturn("userA");
            when(comment.getId()).thenReturn(1L);
            when(userRepository.findByUsername(user.getUsername())).thenReturn(Optional.of(user));
            when(commentRepository.findById(comment.getId())).thenReturn(Optional.of(comment));
            when(likeCommentRepository.existsByUserAndComment(user, comment)).thenReturn(true);
            //when / then
            Assertions.assertThrows(Exception.class,()->likeCommentService.clickFavorite(comment.getId(), user.getUsername()));
        }
    
        @Test
        @DisplayName("좋아요 취소 성공 케이스")
        void cancelFavorite() throws Exception {
            //given
            User user = mock(User.class);
            Comment comment = mock(Comment.class);
            LikeComment likeComment = mock(LikeComment.class);
            when(user.getUsername()).thenReturn("user1");
            when(comment.getId()).thenReturn(1L);
            when(userRepository.findByUsername(user.getUsername())).thenReturn(Optional.of(user));
            when(commentRepository.findById(comment.getId())).thenReturn(Optional.of(comment));
            when(likeCommentRepository.existsByUserAndComment(user, comment)).thenReturn(true);
            //when
            String s = likeCommentService.cancelFavorite(comment.getId(), user.getUsername());
            //then
            Assertions.assertSame("좋아요를 취소하였습니다", s);
        }
    
        @Test
        @DisplayName("좋아요 취소 실패 케이스")
        void cancelFavoriteException() throws Exception {
            //given
            User user = mock(User.class);
            Comment comment = mock(Comment.class);
            LikeComment likeComment = mock(LikeComment.class);
            when(user.getUsername()).thenReturn("user1");
            when(comment.getId()).thenReturn(1L);
            when(userRepository.findByUsername(user.getUsername())).thenReturn(Optional.of(user));
            when(commentRepository.findById(comment.getId())).thenReturn(Optional.of(comment));
            when(likeCommentRepository.existsByUserAndComment(user, comment)).thenReturn(false);
            //when //then
            Assertions.assertThrows(Exception.class,()->likeCommentService.cancelFavorite(comment.getId(), user.getUsername()));
    
        }
    }

박상훈

  1. 빌더 패턴

    ResponseEntity를 return 타입으로 사용하던 중 형식이 조금씩 다른 문제점에 직면

    ex) 1. return new ResponseEntity<>(body,status)

    1. return ResponseEntity.status(HttpStatus.OK).body(responseDto)

    → 생성자 / 빌더패턴의 차이

    → 만약 파라미터의 갯수의 변화가 빈번하거나, 여러 케이스가 필요하다 → 그만큼 생성자가 여러 종류여야함..

    → 빌더패턴 사용 → 유연한 생성자

    → Lombok Builder지원! → 다음 프로젝트에는 꼭 써보자!

  2. Spring Security PermitAll()

    처음 시큐리티의 흐름을 이해했을 때, permitAll()로 인증/인가를 풀어주게 되면 Filter를 아예 거치지 않는 줄 알았다. 하지만 결과적으로 우리가 커스텀해서 addBefore해준 필터를 거치게 되었고, 예기치 못한 Exception이 발생했다.

    아직 정확한 개념은 모르지만 web.ignoring()으로 특정 URL요청에 필터를 거치지 않게 세팅했다.

손혜은

  • ResponseDto 처음에 개인 과제를 할 때 responseDto에 정확히 어떤 내용이 들어가는 게 맞는지에 대한 이해가 부족해서 못 나눴는데 프로젝트를 통해서 필요성과 어떤 값이 들어가는 게 맞는지 알게 됨
  • 댓글을 처음 작성하면 id값이 1이 나와야 하는데 게시글 id값 이어서 나옴 @GeneratedValue(strategy = GenerationType.AUTO) -> @GeneratedValue(strategy = GenerationType.IDENTITY) 해결

장현재

  • 문제-기능구현에 어려움을 느껴 강의를 보고 진행하다보니, 약속된 API명세와 사소한 차이점이 발생하여 코드취합에 장애가 생김.
  • 해결- API명세 피드백 후 코드를 통일하고 재배포

🐥마치며


설계에 시간을 충분히 들였다고 생각했지만, 더 세세하게 짜야 한다는 것을 알게 됐습니다. 또한 실력은 다 다르지만 팀장님을 중심으로 좋은 사람들이 모여 서로를 믿고 배려하면서 진행했습니다. 소통하고 화기애애한 분위기에 속에 프로젝트를 진행할 수 있었습니다. 또한 프로젝트를 통해 팀원 모두가 적어도 하나씩 새로 배우고, 부족했던 부분은 채울 수 있는 시간이 된 것 같습니다.

profile
기록하는 습관

0개의 댓글