본문 바로가기
프로젝트 회고

JWT 인증 기반 뉴스피드 프로젝트: 회고

by taeung515 2025. 6. 5.

API 명세서

 

뉴스피드 프로젝트

ERD 사용된 에러 코드

documenter.getpostman.com

 

원격 코드 저장소

 

GitHub - taeung515/team-newsfeed-project: [ 내일배움캠프 Spring 7기 ] Chapter5. 스프링 팀 프로젝트 - 뉴스피드

[ 내일배움캠프 Spring 7기 ] Chapter5. 스프링 팀 프로젝트 - 뉴스피드 프로젝트 - taeung515/team-newsfeed-project

github.com

 

뉴스피드 프로젝트 개발 및 리팩토링 과정 정리

프로젝트

요구사항

더보기

- 프로필 관리 프로필 조회 프로필 수정 비밀번호 변경 비밀번호 변경 예외처리

- 뉴스피드 게시물 관리 게시물 작성/조회/수정/삭제 뉴스피드 조회

- 사용자 인증 회원가입 회원탈퇴 로그인 (JWT기반)

- 친구(팔로우) 관리 팔로우 팔로우 뉴스피드

- 댓글 기능 댓글 작성/조회/수정/삭제

- 좋아요 기능 (도전) 게시물/댓글 좋아요 및 취소

저는 뉴스피드 프로젝트에서 회원 CRUD, JWT 기반 사용자 인증, 코드 컨벤션에 맞춘 리팩토링을 담당했습니다. 협업 과정에서 개발 충돌을 방지하고 효율성을 높이기 위해 팀원들과 함께 공통 응답 객체 사용, 메서드 네이밍 규칙, DTO 설계 등 세부적인 코드 컨벤션을 정립했습니다. 그 결과 다른 사람이 작성한 코드의 구조와 의도를 쉽게 파악할 수 있었습니다.


1. 프로젝트 구조

  • 기능 중심의 package-by-feature 구조로 설계하여 코드의 명확성과 관리 효율성을 높였습니다.
  • GitHub를 활용하여 기능별로 이슈를 생성하고 dev 브랜치에서 파생된 feature 브랜치를 통해 작업을 진행했습니다.
  • 개발 후 Pull Request를 통한 코드 리뷰와 병합 과정을 거쳐 코드 품질을 유지했습니다.

예시 사진(코드리뷰):


2. 구현 과정

서비스 계층 설계

서비스 계층에서는 self-descriptive한 메서드 이름으로 코드 가독성을 높이고, 비즈니스 로직의 흐름을 직관적으로 표현하는 데 중점을 두었습니다. 반복되는 코드는 별도의 클래스로 추출하거나 메서드를 분리하여 중복을 최소화하는 리팩토링을 진행했습니다.

예시 코드 (회원 비밀번호 수정):

/**
     * 회원 비밀번호 수정
     *
     * @param id          수정할 회원의 ID
     * @param oldPassword 기존 회원의 비밀번호
     * @param newPassword 변경할 비밀번호
     */
    @Override
    @Transactional
    public void updatePassword(Long id, String oldPassword, String newPassword) {

        // 해당 Id를 갖는 유저 조회
        User user = userRepository.findByIdOrElseThrow(id);

        // 비밀번호 일치여부 확인
        passwordValidator.verifyMatch(oldPassword, user.getPassword());

        // 기존 비밀번호와 동일한지 확인. 동일할 경우 변경할 수 없으므로 Exception 발생
        passwordValidator.verifyNotSame(newPassword, oldPassword);

        user.updatePassword(encoder.encode(newPassword));

    }

RESTful API 설계

RESTful API 원칙을 준수하여 엔드포인트 정의 및 HTTP 메서드의 일관성을 유지했습니다.

예시 코드 (비밀번호 변경 API):

@RequestMapping("/api/users")
@PatchMapping("/me/password")
public ResponseEntity<ApiResponse<Void>> updatePassword(@RequestAttribute(RequestAttributeKey.USER_ID) Long id,
                                                        @Valid @RequestBody UpdatePasswordRequestDto requestDto) {
    userService.updatePassword(id, requestDto.getOldPassword(), requestDto.getNewPassword());
    return new ResponseEntity<>(ApiResponse.success("비밀번호 변경이 완료되었습니다."), HttpStatus.OK);
}

Soft Delete 구현

논리적 삭제를 구현하기 위해 User Entity에 @SQLDelete@Where를 사용했습니다. 이는 데이터를 물리적으로 삭제하지 않고 데이터의 무결성을 유지하며 이력 관리와 복구가 용이하기 때문입니다.

@SQLDelete(sql = "UPDATE user SET deleted = true WHERE id = ?")
@Where(clause = "deleted = false")
private Boolean deleted = Boolean.FALSE;

3. 사용자 삭제 시 연관관계 처리 방법

사용자 삭제 시 연관된 자식 엔티티를 처리할 방법은 크게 3가지가 있습니다.

1) DB 차원의 ON DELETE CASCADE

데이터베이스가 직접 연관된 데이터를 삭제합니다.

2) 서비스 계층에서 자식 레포지토리로 처리

서비스 계층에서 연관된 자식 엔티티를 찾아 직접 삭제합니다.

3) 양방향 연관관계를 통한 @CascadeType.REMOVE

엔티티 간의 양방향 연관관계를 설정하고 JPA를 활용하여 자동 삭제합니다.

이 중 저는 첫 번째 방법인 DB 차원의 ON DELETE CASCADE를 선택했습니다. 그 이유는 서비스 계층의 복잡성을 줄이고, 데이터베이스 수준에서 효율적이고 일관된 삭제 처리가 가능하기 때문입니다.

 


4. ServletRequest 활용

JWT 기반 인증을 구현하면서 인증된 사용자의 정보를 ServletRequest에 저장하여 컨트롤러에서 쉽게 접근할 수 있도록 구현했습니다. 이는 스프링 시큐리티 없이 간편한 인증 정보 관리를 위한 초기 구현 방식이었습니다.


5. 필터에서 예외 처리

필터에서 발생한 예외는 @ControllerAdvice가 처리할 수 없기 때문에 필터 내부에서 별도의 예외처리 메서드를 구현하여 JSON 형태의 공통 응답 객체를 통해 일관된 응답을 제공했습니다.


6. 느낀점

이번 프로젝트를 통해 협업과 코드 리뷰의 중요성을 깊이 실감할 수 있었습니다.

특히, 명확한 코드 컨벤션과 체계적인 구조 설계가 프로젝트의 유지보수성과 협업 효율성에 얼마나 큰 영향을 미치는지 직접 경험할 수 있었습니다.

 

저희 팀은 원활한 협업을 최우선 목표로 두고 프로젝트를 진행했습니다.
초기 단계에서 정했던 목표와 컨벤션이 실제 개발 과정에서도 큰 가이드가 되어주었고,
덕분에 코드가 한 사람이 짠 것처럼 일관된 스타일을 유지할 수 있었습니다.

 

메서드 네이밍, DTO 설계 등 세부 규칙까지 팀원들과 꼼꼼히 합의함으로써
리팩토링 과정에서도 다른 사람이 작성한 코드의 의도와 구조를 쉽게 파악할 수 있었고,
내가 담당하지 않은 부분도 자연스럽게 수정하거나 개선할 수 있었습니다.

 

또한, 잘 정리된 API 명세서, 회의 노트 등 다양한 문서 작업이
개발 효율을 크게 높여주고, 전체 개발 기간을 단축하는 데에도 도움이 된다는 것을 알게 되었습니다.

 

이러한 경험을 바탕으로 앞으로도 협업과 소통, 코드 품질 유지에 힘쓰는 개발자가 되도록 더욱 노력하겠습니다.