Skip to content

Comments

fix(#396): 캠페인 제안 상세 조회에 제품 이름 추가#397

Merged
ParkJiYeoung8297 merged 2 commits intodevfrom
fix/#396-add-product-name
Feb 19, 2026
Merged

fix(#396): 캠페인 제안 상세 조회에 제품 이름 추가#397
ParkJiYeoung8297 merged 2 commits intodevfrom
fix/#396-add-product-name

Conversation

@ParkJiYeoung8297
Copy link
Contributor

@ParkJiYeoung8297 ParkJiYeoung8297 commented Feb 19, 2026

fix(#396): 캠페인 제안 상세 조회에 제품 이름 추가

Summary

캠페인 제안 상세조회에서 제품 이름 추가

Changes

  • productName을 응답에 추가함
image

Type of Change

  • Bug fix (기존 기능에 영향을 주지 않는 버그 수정)
  • New feature (기존 기능에 영향을 주지 않는 새로운 기능 추가)
  • Breaking change (기존 기능에 영향을 주는 수정)
  • Refactoring (기능 변경 없는 코드 개선)
  • Documentation (문서 수정)
  • Chore (빌드, 설정 등 기타 변경)
  • Release (develop → main 배포)

Related Issues

#118

참고 사항

브랜드와 제품이 제대로 매칭되어야만 조회가 가능합니다. 만약 브랜드와 제품이 제대로 매칭되지않은 더미 데이터의 경우 productName이 null이 반환됩니다. (데모데이 이후 에러 반환하는 것으로 수정 필요)
또한 데모데이 이후 본인이 보낸 캠페인 제안만 조회 가능하도록 권한 확인 로직도 추가예정입니다.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ParkJiYeoung8297, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 캠페인 제안 상세 조회 시 제품 이름을 응답에 포함시켜 사용자 경험을 향상시키는 것을 목표로 합니다. 기존에는 제품 ID만 제공되었으나, 이제 관련 브랜드의 스폰서십에서 제품 이름을 가져와 상세 응답 DTO에 통합함으로써, 캠페인 제안에 대한 보다 완전하고 이해하기 쉬운 정보를 제공합니다.

Highlights

  • 캠페인 제안 상세 조회 개선: 캠페인 제안 상세 조회 응답에 제품 이름 필드를 추가하여 사용자에게 더 풍부한 정보를 제공합니다.
  • 새로운 리포지토리 메서드 추가: BrandAvailableSponsorRepository에 브랜드 ID와 제품 ID로 BrandAvailableSponsor를 조회하는 findByBrandIdAndId 메서드를 추가했습니다.
  • 서비스 로직 업데이트: CampaignProposalQueryService의 getProposalDetail 메서드에서 제품 ID를 사용하여 제품 이름을 조회하고, 이를 응답 DTO에 전달하도록 로직을 수정했습니다.
  • 응답 DTO 변경: CampaignProposalDetailResponse DTO에 productName 필드를 추가하고, 해당 필드를 설정하는 팩토리 메서드를 업데이트했습니다.
Changelog
  • src/main/java/com/example/RealMatch/brand/domain/repository/BrandAvailableSponsorRepository.java
    • java.util.Optional 임포트가 추가되었습니다.
    • 브랜드 ID와 제품 ID로 BrandAvailableSponsor를 찾는 findByBrandIdAndId 메서드가 추가되었습니다.
  • src/main/java/com/example/RealMatch/business/application/service/CampaignProposalQueryService.java
    • BrandAvailableSponsor 및 BrandAvailableSponsorRepository 임포트가 추가되었습니다.
    • BrandAvailableSponsorRepository가 서비스에 주입되었습니다.
    • getProposalDetail 메서드가 제품 이름을 조회하고 응답 DTO에 전달하도록 수정되었습니다.
  • src/main/java/com/example/RealMatch/business/presentation/dto/response/CampaignProposalDetailResponse.java
    • productName 필드가 추가되었습니다.
    • from 팩토리 메서드의 시그니처가 productName을 인자로 받도록 변경되었습니다.
    • 응답 빌더에 productName이 포함되도록 수정되었습니다.
Activity
  • 이 PR에는 아직 사람의 활동이 없습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This PR adds the product name when querying campaign proposal details. However, it introduces a significant security vulnerability: an Insecure Direct Object Reference (IDOR) in the getProposalDetail method, as it retrieves proposal details without verifying user authorization. An authorization check must be implemented to ensure only authorized users can view details. Additionally, the format of the newly added repository method is inconsistent, and the logic for fetching product names could be refactored for better readability.

Comment on lines 28 to 29
CampaignProposal proposal = campaignProposalRepository.findByIdWithTags(proposalId)
.orElseThrow(() -> new CustomException(BusinessErrorCode.CAMPAIGN_PROPOSAL_NOT_FOUND));
Copy link
Contributor

Choose a reason for hiding this comment

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

security-high high

The getProposalDetail method takes a userId and a proposalId but fails to verify if the user is authorized to access the proposal. An attacker could potentially view any campaign proposal by providing its ID. The code even contains a TODO comment acknowledging the missing authorization check. Implement an authorization check to ensure the userId matches either the senderUserId or receiverUserId of the proposal (or has appropriate administrative privileges).

Suggested change
CampaignProposal proposal = campaignProposalRepository.findByIdWithTags(proposalId)
.orElseThrow(() -> new CustomException(BusinessErrorCode.CAMPAIGN_PROPOSAL_NOT_FOUND));
CampaignProposal proposal = campaignProposalRepository.findByIdWithTags(proposalId)
.orElseThrow(() -> new CustomException(BusinessErrorCode.CAMPAIGN_PROPOSAL_NOT_FOUND));
if (!proposal.getSenderUserId().equals(userId) && !proposal.getReceiverUserId().equals(userId)) {
throw new CustomException(BusinessErrorCode.CAMPAIGN_PROPOSAL_FORBIDDEN);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

추후 권한 확인 로직 추가 예정 (데모데이 이후)

Comment on lines 31 to 42
String productName = null;

// TODO: 데모데이 이후에 해당 제품이 없으면 에러 던지는 방향으로 수정 필요!! + 조회 권한 로직 추가 필요(본인만 조회 가능)
if (proposal.getProductId() != null) {
productName = brandAvailableSponsorRepository
.findByBrandIdAndId(
proposal.getBrand().getId(),
proposal.getProductId()
)
.map(BrandAvailableSponsor::getName)
.orElse(null);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

productName을 가져오는 로직은 Optional 체이닝을 사용하여 더 간결하고 표현력 있게 만들 수 있습니다. 이렇게 하면 명시적인 if 문을 제거하고 코드를 더 간결하게 만들 수 있습니다.

다음과 같이 리팩토링할 수 있습니다:

String productName = Optional.ofNullable(proposal.getProductId())
        .flatMap(productId -> brandAvailableSponsorRepository
                .findByBrandIdAndId(proposal.getBrand().getId(), productId))
        .map(BrandAvailableSponsor::getName)
        .orElse(null);

이 코드는 현재 31, 32, 34-42 라인의 구현을 대체할 수 있습니다.

@ParkJiYeoung8297
Copy link
Contributor Author

/gemini review
너가 말한 사항 반영했어. 이제 더 수정할거 없지?

@ParkJiYeoung8297 ParkJiYeoung8297 self-assigned this Feb 19, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds the product name to the campaign proposal detail view. However, a significant security flaw has been identified: the getProposalDetail method lacks an authorization check, leading to an Insecure Direct Object Reference (IDOR) vulnerability. Security controls must be implemented to prevent unauthorized access to sensitive campaign proposal data before merging to production. Additionally, it is suggested to add comments to clarify the use of Optional.ofNullable for the productId field in the CampaignProposal entity, especially since productId is marked as nullable=false.

Comment on lines +33 to +40
// TODO: 데모데이 이후에 해당 제품이 없으면 에러 던지는 방향으로 수정 필요!! + 조회 권한 로직 추가 필요(본인만 조회 가능)
String productName = Optional.ofNullable(proposal.getProductId())
.flatMap(productId -> brandAvailableSponsorRepository
.findByBrandIdAndId(proposal.getBrand().getId(), productId))
.map(BrandAvailableSponsor::getName)
.orElse(null);

return CampaignProposalDetailResponse.from(proposal);
return CampaignProposalDetailResponse.from(proposal, productName);
Copy link
Contributor

Choose a reason for hiding this comment

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

security-high high

The getProposalDetail method currently lacks an authorization check, leading to an Insecure Direct Object Reference (IDOR) vulnerability. The userId parameter is passed but unused, allowing unauthorized access to campaign proposal details. It is critical to verify that the userId matches either the senderUserId or receiverUserId of the proposal before returning the response. Additionally, please add a comment to clarify why Optional.ofNullable is used for the productId field in the CampaignProposal entity, given that productId is marked as nullable=false.

        if (!proposal.getSenderUserId().equals(userId) && !proposal.getReceiverUserId().equals(userId)) {
            throw new CustomException(BusinessErrorCode.CAMPAIGN_PROPOSAL_USER_MISMATCH);
        }

        String productName = Optional.ofNullable(proposal.getProductId())
                .flatMap(productId -> brandAvailableSponsorRepository
                        .findByBrandIdAndId(proposal.getBrand().getId(), productId))
                .map(BrandAvailableSponsor::getName)
                .orElse(null);

        return CampaignProposalDetailResponse.from(proposal, productName);

@ParkJiYeoung8297 ParkJiYeoung8297 merged commit 8c0ca88 into dev Feb 19, 2026
1 check passed
@ParkJiYeoung8297 ParkJiYeoung8297 deleted the fix/#396-add-product-name branch February 19, 2026 05:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant