TIL 39 [ Spring 기초 프로젝트 ( D-day ) - 발표/마무리, 트러블슈팅 ]

2025. 4. 14. 16:40·[내일배움캠프-Sparta]/Spring 6기
반응형

14:00 ~ 16:00 기초 프로젝트 발표회 

 

SNS 기초 프로젝트 KPT 회고록 보러가기

 

내가 팀에서 발표를 담당하게되어 오늘 발표하는 날을 가졌다. 망설임 없이 잘 진행하여서 다행이었다.

오늘부로 각조마다 발표를 하여, 기초 프로젝트가 마무리되는 날이다. 1주일간 짧은 시간동안 팀 프로젝트가 어떻게 진행되는 협업은 어떻게 하고, 팀 내 규칙을 준수해 어떤 방법으로 프로젝트를 진행하는지, 여러가지를 배운 시간 이었다.

개인 프로젝트로 개발을 진행 했었으면 프로젝트가 시간이 많이 오래 걸렸을 것이다. 하지만 팀원 별로 기능마다 구현을 해서, 개인으로 할때보다 더 빠르게 기능 구현을 빠르게 할 수 있었다.

이제 여기서 성능적으로나 비용면에서 고려까지를 할 수 있다면 더 완벽한 프로젝트가 될 수 있었을 탠데 아쉬움이 남는다.

 

✅트러블 슈팅

1. Merge

 

배경

 

팀원들과 기능별로 브랜치를 따로 파서 작업을 시작하기로 했다.
그리고 어느 정도 기능구현이 완료되었을 때 dev브랜치로 병합 한번 해보기로 결정했다.

처음 팀원들과 협업하는 프로젝트이다 보니 브랜치를 따로 설정해서 작업하는 것도 처음이고 merge 하는 것도 처음이라 merge 할 때 충돌이 일어날까 두려웠다.

팀원들과 회의 끝에 돌이킬 수 없는 문제가 발생할 수도 있으니 튜터님 앞에서 merge를 하기로 결정이 되었다.

 

 

원인

 

아니나 다를까 merge 버튼을 누르자 아래 사진예시와 같이 충돌이 일어났다.

 

 

충돌이 일어난 부분을 더블클릭하게 되면 아래 사진과 같은 창이 뜬다.
같은 줄에 서로 다른 코드가 있기 때문에 나타나는 문제이다.

 

 

 

해결(방법 1)

 

dev브랜치라고 되어 있는 부분이 합치기 전 dev 브랜치에 있는 코드들이고, 맨 오른쪽 기능 브랜치라고 되어 있는 부분이 합치고 싶은 코드들이 있는 곳이다.
그리고 가운데가 최종적으로 합쳐져서 만들어지는 dev 브랜치가 된다.

만약, 충돌이 일어난 코드가 있으면 사진과 같이 추가할지 삭제할지에 대한 선택지가 나온다.

 

 

 

 

위 사진에서와 같이 이전 dev브랜치에서 살리고 싶은 코드가 있으면 >> 버튼을 누르고,
만약 필요가 없어 삭제하려면 X를 누르면 된다.

이 방법은 인텔리제이에서 제공하는 merge 기능으로 너무나도 간편하게 merge를 할 수 있게 해 준다.
그러나 이렇게 merge를 하려면 모든 팀원들이 모여있는 상태에서 서로 이야기를 하면서 해야 수월하게 merge가 된다는 단점이 있다.

만약, 팀원이 없는 상태에서 나 혼자서만 merge를 하려고 할 때 다른 사람의 코드 중 한 부분을 필요 없다 느껴 추가하지 않게 되는 불상사가 발생할 수도 있기 때문이다.

이런 단점을 보완하고, 더 효율적인 작업을 할 수 있게 하는 방법이 git에 PR(Pull & Request)를 보내는 것이다.

 

 

해결(방법 2)

 

 

변경한 내용을 git에 push 하게 되면 위 사진과 같이 Compare & pull request라는 문구가 뜨는 걸 볼 수 있다.
이것을 누르고 들어가면 아래 사진과 같이 나온다.

 

 

여기에 어떠한 내용들을 변경해서 push를 했는지 팀원들이 확인해 줬으면 좋겠는지 이러한 내용을 적고 Create pull request 버튼을 클릭한다.
이후부터는 팀원들이 내가 pr을 요청한 코드에 대해 리뷰하면서 merge를 승인할지, 수정이 필요하면 comment를 남겨 보완할 수 있도록 한다.


 

이런 review 과정을 거치고 merge에 대한 승인이 결정되면 merge가 완료된다.

 

 

협업 시 github를 단순히 원격 저장소의 역할로만 활용하는 게 아닌 코드리뷰를 할 수 있는 소통의 창구로 활용할 수 있다.

 

 

 


 

 

2. ClassCastException 에러

 

배경

 

서로 각자 맡은 기능들을 개발하다가 한번 merge 한 후에 다시 작업을 하기로 결정했다.

merge 할 때 충돌이 많이 일어날 것 같아서 많이 두려웠지만, 생각보다 어려운 충돌은 일어나지 않았다.
그 이유는 각자 기능에 맞는 브랜치명을 따로 파서 작업했기 때문이다.

이후 실행이 잘 되는지 요청을 보내면서 하나하나 확인을 하는 작업을 거쳤다.
이때, 댓글 생성하는 기능에서 에러가 발생했다.

 

 

원인

 

ClassCastException이란 에러가 발생했고, 이를 찾아보니 형변환하는 과정에서 잘못되었다는 것이다.

 

로그인 확인을 하려고 session에 저장된 객체에서 userId를 가져오는 코드이다.

왜 여기에서 오류가 발생했지? 고민하다가 원인을 찾았다.

원인은 AuthController의 로그인 메서드에서 발생하였다.

session의 key는 userId로 설정해 놓고, 정작 값은 Long 타입의 id 값이 아닌 LoginResponseDto 형태의 객체를 저장을 했다.
처음에 팀원분과 상의할 때 sesssion key를 맞추자고 이야기가 나왔고, key는 userId로 했었다.
그러고 나서 '키에 해당하는 값에 어떤 것을 넣어야 할까?, User 객체를 넣어야 하나?, responseDto를 넣어야 하나?, 이 session을 나는 사용을 안 하는데?' 고민을 많이 했다.
결국, Dto를 저장하고 필요한 값을 Dto에서 꺼내와 사용하는 방법을 택했다.

하지만, 다른 기능을 개발하는 팀원은 위 객체에서 꺼내와 사용하는 방법이 아닌 userId 값이 저장되어 있다고 생각하고 기능을 구현한 것이다.

 

 

해결

session의 키 이름이 userId여서 값으로 저장되는 것은 id가 저장되어야 하는 게 맞았다.
팀원이 키 이름을 useId로 설정하자고 했을 때 또는 어떤 값을 넣어야 할지 고민했을 때 혼자서만 고민하지 말고 팀원과 소통했으면 일어날 문제가 아니었던 것이다.

 

협업을 할 때는 팀원과 상시로 소통을 하는 부분이 매우 중요하다고 깨달았다.

 

 


 

 

3. 비밀번호 형식 유효성 검사

 

배경

 

프로젝트 요구사항에 비밀번호는 대소문자를 포함한 영문, 숫자, 특수문자 중 1개 이상을 포함해야 하며, 8글자 이상이어야 한다는 조건이 있었다.
이 조건에 부합하는 검사를 구현하고 팀원들이 구현한 기능들과 merge 하다 보니 에러는 아니지만, 고민되는 부분이 생겼다.

 

 

전개

 

회원가입기능을 구현할 때 비밀번호를 입력받는 부분에서 유효성 검사를 하고, 비밀번호를 수정하는 부분에서도 적합한 비밀번호 형식인지 유효성 검사를 실시한다.
하지만, 회원가입과 비밀번호 수정 기능을 서로 다른 팀원이 개발하다 보니 각자 다른 방법으로 유효성 검사를 구현한 것이다.

 

 

한 명은 RequestDto에 @Pattern을 사용하여 정규표현식으로 비밀번호 형식을 적용하고, RequestDto형태로 넘겨주기 위해 @RequestBody로 입력받을 때 정규표현식에 일치하는지 검사를 한다.

즉, Controller에서 유효성 검사를 @Vaild로 했다.

 

 

 

다른 한 명은 Service에서 정규표현식을 사용하여 비밀번호 형식을 검사하는 메서드를 만들었고, 이를 조건문으로 처리하였다.

 

  • 동일한 검사를 서로 다른 방법으로 구현했다 보니 통일되지 않는다는 문제가 생겼다.

 

 

이를 해결하기 위해 계속해서 회의한 결과 답이 나오지 않았다.

그 이유는 통일성을 해결하고자 하나의 방법으로만 구현하려면 각자의 방법에서 또 다른 통일성이 깨지는 것 같은 문제가 발생했다.

 

 

  • Service에서 구현한 메서드를 사용하려면 @Pattern을 삭제해야 한다.
  • 그런데 어차피 이메일 형식 유효성 검사 때문에 어차피 @Valid를 유지해야 하는 것이다.
  • 그리고 회원가입 기능에서는 위 사진과 같이 모여있으니까 통일성이 되지만, 이메일은 @Valid 사용, 비밀번호는 메서드 사용 이렇게 다르면 통일성이 깨지는 것 같다.

 

 

  • 그렇다고 비밀번호 수정하는 로직에서 2번 빨간 부분에 해당하는 로직만 따로 빼와 @Valid로 처리하는 방법은 오히려 다른 유효성 검사 로직들이 모여서 처리하는 통일성을 깨트린다.
  • 그리고 이렇게 비밀번호와 관련된 유효성 검사들이 모여있으니 가독성도 좋아 건드리기 싫었다.
@Valid를 사용하는 방법과 메서드를 사용하여 Service에서 구현하는 방법 중 어느 방법이 더 좋거나 나쁜 방법이라고 할 수는 없다. 두 방법 중에서 선택은 자유지만 사용하려면 하나의 방법만을 사용해서 통일성을 지켜주는 게 좋다.

 

 

여기서 또 한 번 협업 시 소통의 중요함을 느낀다.

 

 

 


 

 

4. 연관관계 (양방향 관계 설정)

 

배경

 

기존에는 @Manytoone 어노테이션을 사용해서 단방향 연관관계를 설정해서 CRUD를 구현했었다.
하지만, 이번에는 친구 요청 보낸 user와 친구 요청을 반는 user가 따로 존재해야 해서 양방향 연관관계를 설정해야 하는 상황이 발생했다.
양방향 연관관계는 사용해 보지 않아 일단 단방향 관계로 설정하고 기능들을 구현했다.

이렇게 단방향이다 보니 친구요청 보내기, 친구 요청 목록 조회, 요청 수락, 요청 거절, 요청받은 목록 조회는 구현이 가능했다.
하지만, 요청을 수락한 친구들 목록을 조회하고, 거기서 삭제하는 기능을 어떻게 구현해야 할지.... 많은 고민이 들었다.

 

 

원인

 

먼저, 응답받는 부분을 Dto 형태로 가져오고 싶었지만, Dto 형태로 작업하다 보니 난생처음 보는 Exception을 만났다.
처음에는 인텔리제이가 문제없이 작동되길래 기쁜 마음으로 postman을 활용해서 요청을 보내 보았다.
친구 요청이 1개가 왔을 땐 수락하고 나서 친구 목록을 조회하면 정상적으로 작동하길래 좋았었다.
하지만, 문제는 요청이 2개 이상이 될 때부터였다.
NonUniqueResultException: Query did not return a unique result: 3 results were returned 이란 에러가 뜨더니 목록이 조회가 안 되는 것이다!!!
NonUniqueResultException을 검색해 보니까 JPA에서 단일 결과를 반환해야 하는 쿼리가 여러 결과를 반환했을 때 발생하는 에러이다.
즉, 쿼리 조건에 맞는 데이터가 3개 이상 존재하며, 이를 단일 객체로 처리할 수 없어서 예외가 발생한 것이다

 

 

해결

 

이 방법 저 방법 사용하다 보니 결국 요청을 수락받은 친구들 목록을 조회하는 기능은 아래 사진과 같이 구현했다.

Controller 부분

Service 부분

 

반환을 Dto 형태가 아닌 String으로 바꾸고 Service 로직에서 변경된 사항들이 있다.

아래 순서로 로직을 구현하였다.
  • 1. 유저 조회
User userByNicknameOrElseThrow = userRepository.findUserByNicknameOrElseThrow(nickname);

 

 

  • 2. 친구 요청을 보낸 FriendResponseDto 타입의 List에 친구 수락 조건을 filter로 걸러주고 조회
List<FriendResponseDto> list =
                friendRepository.findByUserrequest(userByNicknameOrElseThrow)
                        .stream()
                        .filter(friend -> friend.getStatus().equals(ACCEPTED)) // 친구 요청을 수락했는지 확인
                        .map(friend -> new FriendResponseDto(friend))
                        .toList();

 

 

  • 3. 위 리스트의 FriendResponseDto 타입에서 nickname만 가져와 다시 String형 List에 추가
List<String> nicknames = list.stream()
                .map(friendResponseDto -> friendResponseDto.getReceiverNickname()) // FriendResponseDto에서 nickname 가져오기
                .toList();

 

 

  • 4. nickname이 저장된 리스트 반환
return nicknames;

 

 

또 다른 문제

 

일단 구현하기 위해 이런 형식으로 하다 보니 친구 삭제와 목록에서 또 문제가 발생했다.

 

 

예를 들어 친구 1이 친구 2에게 친구신청을 요청을 보내고 친구 2가 수락을 받게 되어 친구관계가 된다면
친구 1의 친구목록을 조회하면 친구 2가 잘 조회된다.
하지만!
친구 2에게는 친구 1이 친구목록에서 조회되지 않는다....

 

그리고 위 사진과 같이 JPA에서 제공해 주는 delete 메서드는 Friend Entity 객체 타입을 파라미터로 받는다.
하지만, 여기서 친구목록을 Friend 객체가 아닌 String형의 List로 설정하다 보니 이렇게 문제가 생겼다....

 

 

 

해결

 

양방향 연관관계를 설정해 줌으로써 해결할 수 있었다.

 

 

user Entity

friend Entity

 

  • 이전에 없던 User Entity에 @oneToMany라는 어노테이션을 사용하고, userrequest 또는 userreceiver를 매핑하도록 하여 양방향으로 연관관계를 설정하였다.

 

  • LoginUser 인터페이스를 만들어

 

  • @LoginUser 어노테이션을 사용해 로그인된 유저가 본인인지 확인하는 기능을 구현했다.

 

 

 

  • 그리고 위 사진과 같이 user1(로그인된 본인)이 user2와 친구관계인지 확인하는 로직과 user2가 user1과 친구관계인지 확인하는 로직을 구현하여 삭제하도록 구현했다.
  • 그리고 delete 하는 객체도 Friend로 받아오도록 구현했다.

 

이렇게 구현하니 친구삭제 요청도 성공적으로 작동하고, 친구목록에서도 삭제가 된다.

 

 

양쪽에서 관계를 탐색하고 관련 문제를 신중하게 관리해야 할 필요가 있는 경우에는 양방향으로 연관관계를 설정하여 매핑할 수 있도록 해야 한다.

 

반응형

'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글

TIL 41 - [ Spring 심화 ( API 예외처리, JPQL, Fetch Join ) ]  (0) 2025.04.21
TIL 40 - [ Spring 심화 ( HttpMethodConverter, Converter, Formatter ) ]  (0) 2025.04.15
TIL 38 [ Spring 기초 프로젝트 ( Day 5 ) - 마무리 작업 ( ppt, 대본, ERD/API명세서 리뉴얼) ]  (0) 2025.04.11
TIL 37 [ Spring 기초 프로젝트 ( Day 4 ) - 구현 ( 게시판 댓글 기능 연동 ), 스탠다드반 ( JPA 성능 최적화 ) ]  (0) 2025.04.10
TIL 36 [ Spring 기초 프로젝트 ( Day 3 ) - 구현 ( 게시판 - 좋아요 기능연동 ) ]  (0) 2025.04.09
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • TIL 41 - [ Spring 심화 ( API 예외처리, JPQL, Fetch Join ) ]
  • TIL 40 - [ Spring 심화 ( HttpMethodConverter, Converter, Formatter ) ]
  • TIL 38 [ Spring 기초 프로젝트 ( Day 5 ) - 마무리 작업 ( ppt, 대본, ERD/API명세서 리뉴얼) ]
  • TIL 37 [ Spring 기초 프로젝트 ( Day 4 ) - 구현 ( 게시판 댓글 기능 연동 ), 스탠다드반 ( JPA 성능 최적화 ) ]
dimenshun
dimenshun
한 소년의 개발 일기
    반응형
  • dimenshun
    Dev Life Notes
    dimenshun
  • 전체
    오늘
    어제
    • 분류 전체보기 (268)
      • CS (23)
        • 자료구조 (0)
        • 알고리즘 (0)
        • 컴퓨터 구조 (8)
        • 네트워크 (6)
        • 운영체제 (3)
        • DB ( + SQLD ) (5)
        • SW공학 (1)
      • 프로그래밍 (3)
        • Java (0)
        • Spring (0)
        • HTML,CSS (3)
        • JavaScript (0)
      • 개발 툴 (7)
        • Git(버전관리) (1)
        • Docker (3)
        • AWS (2)
        • JSP (1)
      • 코딩테스트(Algorithm) (125)
        • 백준 (6)
        • 프로그래머스 (119)
      • [내일배움캠프-Sparta] (110)
        • Spring 6기 (106)
        • KPT 회고 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Til
    It
    코딩테스트
    배포
    AWS
    메모리
    운영체제
    내일배움캠프
    컴퓨터구조
    docker
    spring
    Testcode
    CPU
    개발자
    웹
    KPT
    SQLD
    네트워크
    db
    백엔드
    세션
    OS
    알고리즘
    web
    SQL
    Python
    cs
    network
    트랜잭션
    Java
  • hELLO· Designed By정상우.v4.10.3
dimenshun
TIL 39 [ Spring 기초 프로젝트 ( D-day ) - 발표/마무리, 트러블슈팅 ]
상단으로

티스토리툴바