1 of 26

Project Feed-a(피다)

발표자 최경진

Team 6pring(식스프링)

2 of 26

CONTENTS

팀 및 프로젝트 소개

주요 기능 및 API

프로젝트 진행 과정

팀원별 트러블슈팅

사용 기술 및 구조

팀원별 회고

01

04

02

05

03

06

3 of 26

팀 소개

최경진

김나경

안요한

이의현

팀장

팀원

팀원

팀원

프로필 기능 구현

게시글 댓글 API 구현

회원 관리 기능 구현

Entity, Repository 설계

게시글 댓글/좋아요 API 구현

전역 예외 처리

게시글 기능 구현

게시글 좋아요 API 구현

팔로우 기능 구현

페이징 처리

테스트 코드 작성 (일부분)

게시글 리팩토링

4 of 26

프로젝트 소개

개발자, 학생, 그리고 자기 계발에 관심 있는 모두가 모여

서로의 스터디 정보와 노하우를 나누는 커뮤니티 SNS입니다.

스터디 모집 및 경험 공유

질문과 답변을 통한 지식 나눔

성장과 동기부여를 위한 소통 공간

함께 배우고, 성장하는 즐거움을 경험 시켜 드릴수가 있을 것 같습니다.

피다(Feed-a)

5 of 26

프로젝트 진행 과정

역할 분담

와이어 프레임 작성

ERD, API 명세서 작성

Git 전략 수립

프로필 관리

뉴스피드 게시물 관리

사용자 인증 �친구 관리

검색 및 정렬 기능

댓글

좋아요

PPT 자료 준비

시연 영상 자료 준비

발표 준비

프로젝트 계획

필수 기능 구현

도전 기능구현

발표 준비

01

02

03

04

6 of 26

사용 기술 및 구조

  • Java 17
  • Spring Boot 3.5.0
  • Gradle 8.5
  • Spring Security + JWT 인증/인가
  • Spring Data JPA (Hibernate 6.6.13.Final)
  • MySQL 8.0 이상
  • Redis Cloud

핵심 기술 스택

  • 도메인 중심 패키징: 유지보수가 쉬운 구조를 위해 기능별로 분리하여 설계�
  • JWT 기반 인증/인가 구현: 인증 정보를 토큰에 담아 stateless한 방식으로 인증 처리�
  • 전역 예외 처리 적용: 사용자에게 일관된 에러 메시지를 전달하도록 설계�

7 of 26

ERD

8 of 26

와이어 프레임

9 of 26

구조

  • 도메인별 기능과 데이터를 패키지로 묶어 응집도 강화
  • 도메인 내 Controller, Service, Repository 포함
  • 공통 기능은 별도 패키지로 분리
  • 도메인별 관심사 분리가 명확해 코드 가독성과 유지보수성 향상
  • 팀 단위 역할 분담과 협업 효율성 증대

도메인 중심 패키징

10 of 26

구조

  • @ControllerAdvice로 전역 예외 처리기 구현
  • 커스텀 예외 클래스 정의로 상황별 예외 관리
  • 공통 예외 처리로 중복 코드 최소화
  • 예외 처리 로직 중앙 집중화로 유지보수 편리
  • 사용자 친화적이고 명확한 에러 메시지 제공
  • 시스템 안정성 강화

전역 예외 처리 + 커스텀 예외 응답

11 of 26

사용 기술

  • JWT 인증 필터를 Spring Security 필터 체인에 추가하여 토큰 검증
  • 토큰에서 사용자 정보 추출 후 SecurityContext에 인증 정보 설정
  • 세션 없이 Stateless 인증 구조 적용
  • 토큰 유효성 및 만료 검사로 보안 강화

Spring Security + JWT Filter

12 of 26

사용 기술

  • Access Token 블랙리스트 처리 구현
  • Redis의 TTL(Time To Live) 기능을 활용해 자동 만료 관리
  • Redis Cloud를 사용하여 별도 설치/운영 없이 간편하게 시스템 구축
  • 높은 가용성과 빠른 응답 속도 제공
  • 애플리케이션 서버와 분리된 Redis Cloud에 토큰 상태를 저장하여 확장성과 유지보수성을 높임

Redis Cloud

13 of 26

Git (1)

  • Github Organzation 기반 협업

- 동일 Organization을 통한 권한 및 Branch 관리

  • create-issue-branch 도입

- issue 생성 => branch 자동 생성 github action 로직을 통해 명칭을 통일하고 개발 흐름을 효율적으로 확인

🛠 Github 전략 및 협업 방식 (1)

14 of 26

Git (2)

  • 코드 리뷰

- Pull Request 개발 브랜치 develop 병합 리뷰어를 최소 2인으로 제한

- 팀원의 병합 승인 이후 병합 가능

=> 서로의 코드 이해 향상 및 병합 전 오류 최소화

🛠 Github 전략 및 협업 방식 (2)

15 of 26

  • 게시글 작성
  • 게시글 단일 조회
  • 게시글 전체 조회 (검색, 페이징)
  • 게시물 전체 조회 API (팔로잉한 사람들)
  • 게시글 수정
  • 게시글 삭제

게시글 기능

HTTP 메서드

URL

POST

/api/posts

GET

/api/posts/{id}

GET

/api/posts?page={page}&size={size}&keyword={keyword}&startUpdatedAt={startUpdatedAt}&endUpdatedAt={endUpdatedAt}

GET

/api/posts/followings?page={page}&size={size}

PUT

/api/posts/{id}

DELETE

/api/posts/{id}

주요 기능 및 API

16 of 26

주요 기능 및 API

사용자 인증 기능

  • 회원 가입
  • 회원 탈퇴
  • 비밀번호 수정
  • 로그인
  • 로그아웃

HTTP 메서드

URL

POST

/api/accounts

DELETE

/api/accounts/me

PATCH

/api/accounts/password

POST

/api/accounts/login

POST

/api/accounts/logout

17 of 26

  • 프로필 조회
  • 전체 프로필 조회 (검색,페이징)
  • 프로필 수정

프로필 기능

HTTP 메서드

URL

POST

회원 가입시 자동 생성

GET

/api/profiles/{id}

GET

/api/profiles?page={page}&size={size}

PUT

/api/profiles/me

주요 기능 및 API

18 of 26

팔로우 기능

  • 특정 유저 팔로우
  • 특정 유저 언팔로우
  • 본인 팔로우 목록 조회(페이징)
  • 본인 팔로워 목록 조회(페이징)
  • 팔로우 목록 조회
  • 팔로워 목록 조회

HTTP 메서드

URL

POST

/api/follows/{profileId}

DELETE

/api/follows/{followingId}

GET

/api/follows/followings?page={page}&size={size}

GET

/api/follows/followers?page={page}&size={size}

GET

/api/follows/{profiileId}/followings?page={page}&size={size}

GET

/api/follows/{profiileId}/followers?page={page}&size={size}

주요 기능 및 API

19 of 26

  • 특정 게시글에 댓글 작성
  • 댓글 단건 조회
  • 특정 게시글 댓글 전체 조회
  • 댓글 내용 수정
  • 댓글 삭제

게시글 댓글 기능

HTTP 메서드

URL

POST

회원 가입시 자동 생성

GET / 단건

/api/comments/{id}

GET / 다건

/api/comments/posts/{postId}

PATCH

/api/comments/{id}

DELETE

/api/comments/{id}

주요 기능 및 API

20 of 26

영상 시연

21 of 26

팀원별 트러블슈팅

Spring Security + JWT 예외 처리 문제

상황

  • JWT 검증 통과 → 컨트롤러 도달 → 서비스에서 예외 발생

  • 하지만 클라이언트에는 Security 인증 실패 메시지 응답

원인

  • 예외 발생 시,

Spring MVC 가 내부적으로 /error 경로로 foward 함

  • 하지만, /error 경로가 Security 에서 보호되고 있기 때문에

Security 인증 실패 메시지가 응답됨

해결 방법

  • /error 접근 허용: requestMatchers("/error").permitAll() 추가

  • @ControllerAdvice를 사용한 예외 직접 처리

22 of 26

팀원별 트러블슈팅

문제 요약

기존에는 단순히 profileId만 받아서 좋아요를 삭제했기 때문에,� 다른 사용자의 profileId를 알기만 하면 누구든 삭제 요청이 가능한 보안 취약점(IDOR: Insecure Direct Object Reference)이 발생함.

해결

JWT 인증 정보를 활용하여 현재 로그인한 사용자의 profileId를 서버에서 직접 추출하고, 해당 사용자만 자신의 좋아요를 삭제할 수 있도록 함.

@DeleteMapping("/{id}/likes")

public ResponseEntity<PostResponseDto> deleteLikes(

@PathVariable Long id,

@AuthenticationPrincipal JwtPayload jwtPayload) {

Long profileId = jwtPayload.getProfileId(); // 현재 로그인한 사용자 ID 추출

// 인증된 사용자만 자신의 좋아요 삭제 가능

return new ResponseEntity<>(postService.deleteLikes(id, profileId), HttpStatus.OK);}

흐름: 단순히 profileId를 받아서 삭제하기 때문에,

다른 사용자의 profileId를 알고 있으면

그 사람의 좋아요도 삭제할 수 있게 되어 보안상 문제 발생

수정자 권한 미검증으로 인한 수직적 접근 통제 실패

23 of 26

팀원별 트러블슈팅

원인

Java

// 문제된 코드

@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDateTime startUpdatedAt

  • @DateTimeFormat의 패턴(yyyy-MM-dd)과 타입(LocalDateTime) 불일치
  • LocalDateTime은 시분초 필요, 패턴은 날짜만 포함

해결

Java

// 수정된 코드

@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startUpdatedAt

문제

기간 조회 API 구현 중 StringLocalDateTime 변환 에러 발생

LocalDateTime -> LocalDate 변경

DB로직에서는 LocalDate -> LocalDateTime 타입변경하여 수행

LocalDateTime 형식 변환 문제

24 of 26

팀원별 트러블슈팅

// 문제 코드

GetProfileResponseDto dto = new GetProfileResponseDto();

dto.setAccountId(profile.getAccountId()); // setter 사용

해결: 생성자 기반 불변 객체로 통일

// 개선 코드

GetProfileResponseDto dto = new GetProfileResponseDto(

profile.getAccountId(),

profile.getNickname(),

profile.getBirth()

);

문제:

setter와 생성자 방식이 혼재되어 컴파일 에러 발생

DTO 필드 주입 방식 혼동

25 of 26

팀원별 회고

최경진:

김나경:

이의현:

안요한:

지금까지 개인 프로젝트만 진행했었기에 다른 팀원분들의 코드를 리뷰하며 몰랐던 부분을 확인하고, 비슷한 코드라도 더 좋은 방식이 있는 것을 알게 되어 유익한 시간이었습니다.

전체 프로젝트를 설계하고 구현하는 맥락을 처음부터 끝까지 차근차근 모두 배워나갈 수 있는 시간이었습니다.

서로의 코드를 직접 보며 의견을 나눈 과정이 큰 도움이 되었습니다. 각기 다른 접근 방식과 개선할 점을 발견하면서 부족한 부분도 깨닫게 되었고, 함께 성장할 수 있는 소중한 시간이었습니다.

팀 프로젝트를 통해 설계부터 구현까지의 전체 개발 과정을 체계적으로 학습하고, 다양한 접근 방식과 효율적인 코딩 기법을 익힐 수 있었습니다. 특히 스프링 학습 과정에서 팀원들과의 코드 리뷰와 협업을 통해 함께 성장하는 값진 경험을 얻었습니다.

26 of 26

감사합니다

발표자 최경진

Team 6pring(식스프링)