Skip to content

Conversation

@DreamPaste
Copy link
Member

📋Summary

자유게시판(Freeboard)과 투표게시판(Voteboard) API의 응답 구조를 개선하여 프론트엔드 사용성을 향상시키고 API 일관성을 확보했습니다.

  • 자유게시판 목록 조회 — thumbnailUrl & imageCount 실제 동작 구현
  • 투표게시판 목록 조회 - thumbnailUrl & imageCount 추가
  • 투표게시판 isLiked/liked 필드 중복 제거
  • Swagger 문서 개선 - 투표게시판 상세 조회(시나리오별 예시 추가)
  • Orval 타입 호환성 확보

🎯 완료된 작업 목록

1. 자유게시판 목록 조회 — thumbnailUrl & imageCount 실제 동작 구현

문제점: 기존에는 TODO 주석과 함께 null/0 값만 반환

해결 방법:

  • Querydsl 서브쿼리를 사용하여 DB 레벨에서 효율적으로 집계
  • 첫 번째 이미지(sequence 최소값)를 썸네일로 추출
  • N+1 쿼리 문제 방지

변경 파일:

  • PostRepositoryImpl.java: 서브쿼리 추가
  • FreeboardServiceImpl.java: 실제 값 사용
// 서브쿼리: 첫 번째 이미지 URL 조회 (sequence 순서로 정렬)
QPostImage subImage = new QPostImage("subImage");
var thumbnailUrlSubQuery = JPAExpressions
    .select(subImage.imageUrl.min())
    .from(subImage)
    .where(subImage.post.id.eq(post.id)
        .and(subImage.sequence.eq(
            JPAExpressions
                .select(subImage.sequence.min())
                .from(subImage)
                .where(subImage.post.id.eq(post.id))
        )));

// 서브쿼리: 이미지 개수 조회
QPostImage countImage = new QPostImage("countImage");
var imageCountSubQuery = JPAExpressions
    .select(countImage.count().intValue())
    .from(countImage)
    .where(countImage.post.id.eq(post.id));

2. 투표게시판 목록 조회 — thumbnailUrl & imageCount 추가

구현 내용:

  • VotePostSummaryResponsethumbnailUrl, imageCount 필드 추가
  • VotePostMapper에서 이미지 리스트를 sequence 기준으로 정렬하여 첫 번째 이미지 추출

변경 파일:

  • VotePostSummaryResponse.java: 필드 추가
  • VotePostMapper.java: 매핑 로직 구현
// 이미지 정보 추출
List<VotePostImage> images = votePost.getImages();
String thumbnailUrl = images.isEmpty() ? null :
    images.stream()
        .sorted((img1, img2) -> Integer.compare(img1.getSequence(), img2.getSequence()))
        .findFirst()
        .map(VotePostImage::getImageUrl)
        .orElse(null);
int imageCount = images.size();

3. 투표게시판 isLiked/liked 필드 중복 제거

문제점: Lombok의 boolean 타입 getter가 isLiked()getLiked() 두 개를 생성하여 JSON에 isLikedliked 모두 포함

해결 방법:

  • @JsonProperty("isLiked") 어노테이션 추가
  • 타입을 booleanBoolean으로 변경하여 비로그인 사용자는 null 처리

변경 파일:

  • VotePostDetailResponse.java
  • VotePostSummaryResponse.java
@Schema(description = "현재 사용자의 좋아요 여부 (비인증 사용자인 경우 null)")
@JsonProperty("isLiked")
private Boolean isLiked;

4. 투표게시판 상세 조회 — 권한 필드 추가

추가된 필드:

  • isAuthorized: 액세스 토큰 제공 여부 (인증 상태)
  • isAuthor: 작성자 여부
  • canEdit: 수정 권한 (작성자면 true, 비인증이면 null)
  • canDelete: 삭제 권한 (작성자면 true, 비인증이면 null)

변경 파일:

  • VotePostDetailResponse.java: 필드 추가
  • VotePostMapper.java: 권한 계산 로직 추가
  • VotePostServiceImpl.java: userId 전달
// 인증 여부 확인
boolean isAuthorized = userId != null;

// 작성자 여부 확인
boolean isAuthor = userId != null && votePost.getUser().getId().equals(userId);

return VotePostDetailResponse.builder()
    .isAuthorized(isAuthorized)
    .isAuthor(isAuthor)
    .canEdit(isAuthorized ? isAuthor : null)
    .canDelete(isAuthorized ? isAuthor : null)
    // ... 기타 필드
    .build();

5. Swagger 문서 개선 — 투표게시판 상세 조회

개선 내용:

  • 4가지 시나리오별 예시 추가
  • 각 필드의 nullable 명시
  • 상세한 설명 추가

4가지 예시:

  1. 인증 사용자 (본인 작성, 투표 전)
    • isAuthorized: true
    • isAuthor: true
    • canEdit: true, canDelete: true
    • selectedOptionIds: []
  2. 인증 사용자 (다른 사람 글, 투표 완료)
    • isAuthorized: true
    • isAuthor: false
    • canEdit: false, canDelete: false
    • selectedOptionIds: [4, 6]
  3. 비인증 사용자
    • isAuthorized: false
    • isAuthor: false
    • isLiked: null, canEdit: null, canDelete: null
  4. 투표 완료된 게시글
    • voteStatus: "COMPLETED"
    • 투표 결과 확정

변경 파일:

  • VoteboardController.java: @Operation, @ApiResponses 개선

6. Orval 타입 호환성 확보

개선 내용:

  • 모든 DTO에 Swagger @Schema 어노테이션 명시
  • nullable 필드 정확히 표시 (nullable = true)
  • 일관된 네이밍 컨벤션 적용

예시:

@Schema(
    description = "현재 사용자의 좋아요 여부 (비인증 사용자인 경우 null)",
    example = "true",
    requiredMode = Schema.RequiredMode.REQUIRED,
    nullable = true
)
@JsonProperty("isLiked")
private Boolean isLiked;

📊 테스트 결과

전체 테스트 통과율

  • 통과: 254개
  • 스킵: 9개 (S3IntegrationTest — Testcontainers 관련)
  • 실패: 0개
  • 통과율: 100% (실행된 테스트 기준)

주요 테스트 케이스

1. FreeboardServiceTest

  • ✅ 카테고리별 게시글 목록 조회
  • ✅ thumbnailUrl 검증
  • ✅ imageCount 검증
  • ✅ 내용 미리보기 생성

2. VoteboardLikeInfoIntegrationTest

  • ✅ 게시글 상세 조회 시 좋아요 정보 포함 (좋아요 없는 상태)
  • ✅ 게시글 상세 조회 시 좋아요 정보 포함 (좋아요 있는 상태)
  • ✅ 게시글 목록 조회 시 좋아요 정보 포함
  • ✅ 비로그인 사용자의 게시글 상세 조회 — isLiked는 null
  • ✅ 좋아요 토글 후 상세 조회 — likeCount와 isLiked 변경 확인

3. FreeboardIntegrationTest

  • ✅ 게시글 생성 및 조회
  • ✅ 인증 없이 댓글 작성 시도 (401 에러)
  • ⏸️ 이미지 업로드 테스트 (S3 환경 필요 — 주석 처리)

🔄 API 응답 변경 사항

자유게시판 목록 조회 (Before → After)

Before

{
  "posts": [
    {
      "postId": 123,
      "title": "테스트 게시글",
      "thumbnailUrl": null,
      "imageCount": 0
    }
  ]
}

After

{
  "posts": [
    {
      "postId": 123,
      "title": "테스트 게시글",
      "thumbnailUrl": "https://s3.amazonaws.com/bucket/freeboard/image1.jpg",
      "imageCount": 3,
      "updatedAt": "2025-01-15T14:30:00"
    }
  ]
}

투표게시판 상세 조회 (Before → After)

Before (인증 사용자)

{
  "id": 1,
  "title": "투표 게시글",
  "isLiked": false,
  "liked": false
}

After (인증 사용자 — 작성자)

{
  "id": 1,
  "title": "투표 게시글",
  "isLiked": false,
  "isAuthorized": true,
  "isAuthor": true,
  "canEdit": true,
  "canDelete": true
}

After (비인증 사용자)

{
  "id": 1,
  "title": "투표 게시판",
  "isAuthorized": false,
  "isAuthor": false
}

Note: isLiked, canEdit, canDelete는 null이므로 JSON에 포함되지 않음


🚀 프론트엔드 영향도

1. 자유게시판 목록

// Orval 생성 타입 (예상)
interface FreeboardSummary {
  postId: number;
  title: string;
  thumbnailUrl?: string;  // 이제 실제 값 제공
  imageCount: number;     // 이제 실제 값 제공
  updatedAt: string;
}

// 사용 예시
<img src={post.thumbnailUrl || '/default-image.jpg'} />
<span>{post.imageCount}개의 이미지</span>

2. 투표게시판 상세

interface VotePostDetail {
  id: number;
  isLiked: boolean | null;
  isAuthorized: boolean;
  isAuthor: boolean;
  canEdit: boolean | null;
  canDelete: boolean | null;
}

// 권한 기반 UI 렌더링
{votePost.canEdit && <EditButton />}
{votePost.canDelete && <DeleteButton />}
{votePost.isAuthorized && <LikeButton isLiked={votePost.isLiked} />}

@linear
Copy link

linear bot commented Nov 28, 2025

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review any files in this pull request.

@DreamPaste DreamPaste self-assigned this Nov 28, 2025
@DreamPaste DreamPaste added Docs 📝 내용을 문서화하거나 주석을 추가합니다. Feat 💡 새로운 기능을 구현하고 추가합니다! 휘건 labels Nov 28, 2025
@github-actions
Copy link

@github-actions
Copy link

github-actions bot commented Nov 28, 2025

📦 번들 분석 결과

📊 번들 크기 요약

항목
📦 전체 번들 크기 3.7M
📄 JavaScript 크기 1.5M
🗂️ JavaScript 파일 수 57개

🔍 주요 청크 파일 (크기순)

fe98bb7c-75056deddb8826d9.js - 169K
framework-69e0f7d37422957b.js - 137K
main-aef2774224ba47b6.js - 130K
7147-4b792b1c613a0a9e.js - 122K
1762-0e7232d83dcb3887.js - 121K
polyfills-42372ed130431b0a.js - 110K
2877-a44387a54a9dad43.js - 86K
6086-65069d4634f32053.js - 76K
page-3026af1cfd56a766.js - 31K
2906-230d0473dd8a9533.js - 28K

🤖 자동 생성된 번들 분석 리포트

@github-actions
Copy link

github-actions bot commented Nov 28, 2025

⚡ Lighthouse 성능 분석 결과

📊 전체 평균 점수

지표 점수
🚀 Performance 74점
♿ Accessibility 86점
✅ Best Practices 100점
🔍 SEO 100점

📈 측정 현황

  • 측정 성공: 15/16 페이지
  • 상태: success

📄 페이지별 상세 분석

🏠 커뮤니티 페이지: /main/community

지표 점수
🚀 Performance 70점
♿ Accessibility 78점
✅ Best Practices 100점
🔍 SEO 100점

📊 상세 분석 보기

👥 창업자 페이지: /main/founder

지표 점수
🚀 Performance 75점
♿ Accessibility 87점
✅ Best Practices 100점
🔍 SEO 100점

📊 상세 분석 보기

🏡 홈 페이지: /main/home

지표 점수
🚀 Performance 75점
♿ Accessibility 91점
✅ Best Practices 100점
🔍 SEO 100점

📊 상세 분석 보기

🗺️ 지도 페이지: /main/maps

지표 점수
🚀 Performance 75점
♿ Accessibility 87점
✅ Best Practices 100점
🔍 SEO 100점

📊 상세 분석 보기

👤 프로필 페이지: /main/profile

지표 점수
🚀 Performance 75점
♿ Accessibility 88점
✅ Best Practices 100점
🔍 SEO 100점

📊 상세 분석 보기

🔗 전체 상세 분석 결과

📊 전체 상세 Lighthouse 분석 결과 보기

📄 측정된 페이지

  • /main/community
  • /main/founder
  • /main/home
  • /main/maps
  • /main/profile

모든 페이지에서 성능 측정이 완료되었습니다.


🤖 자동 생성된 Lighthouse 성능 리포트

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

@DreamPaste DreamPaste merged commit b85a793 into dev Dec 3, 2025
4 checks passed
@DreamPaste DreamPaste deleted the feat/SOS-44-api-update branch December 3, 2025 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Docs 📝 내용을 문서화하거나 주석을 추가합니다. Feat 💡 새로운 기능을 구현하고 추가합니다! 휘건

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants