Skip to content

[REFACTOR] 현상관리 페이지 2차 QA 반영#256

Merged
jeonbinggu merged 6 commits intodevelopfrom
refactor/photo-manange-qa-2-pm031(#202)
Feb 12, 2026
Merged

[REFACTOR] 현상관리 페이지 2차 QA 반영#256
jeonbinggu merged 6 commits intodevelopfrom
refactor/photo-manange-qa-2-pm031(#202)

Conversation

@jeonbinggu
Copy link
Contributor

@jeonbinggu jeonbinggu commented Feb 12, 2026

🔀 Pull Request Title

현상관리 페이지 2차 QA 반영


🎞️ 주요 코드 설명

주제1

주제2


📌 PR 설명

이번 PR에서 어떤 작업을 했는지 요약해주세요.

  • PM-043 배송지 상세 정보 입력하기
  • PM-050 CTA 확인(그냥 고정으로)
  • PM-051 store 만들어서 상태 저장
  • PM-060 복사 토스트 상하 패팅 맞게
  • popup 외부 클릭 시 비활성화
  • PM-060 뒤로가기 시 clean 적용

📷 스크린샷

UI 변경이 있을 경우 스크린샷을 첨부해주세요.


@jeonbinggu jeonbinggu self-assigned this Feb 12, 2026
@jeonbinggu jeonbinggu added design UI, 디자인 변경 feature 새 기능 추가 refactor 내부 구조 개선(가독성,확장성,유지보수성) labels Feb 12, 2026
@vercel
Copy link

vercel bot commented Feb 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
finders Ready Ready Preview, Comment Feb 12, 2026 2:46pm

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jeonbinggu, 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은 현상관리 페이지의 2차 QA 반영을 목표로, 사용자 경험을 개선하기 위한 여러 기능 및 UI/UX 개선 사항을 포함합니다. 특히 주소 입력 및 인화 옵션 선택 과정의 상태 관리 로직을 Zustand 스토어를 활용하여 더욱 견고하게 만들었으며, 실시간 가격 견적 시스템을 도입하여 사용자에게 즉각적인 피드백을 제공합니다. 이를 통해 사용자가 더 직관적이고 오류 없이 주문 과정을 진행할 수 있도록 지원합니다.

Highlights

  • 상세 주소 입력 페이지 추가: 사용자가 기본 주소 선택 후 상세 주소(배송지명, 상세 주소)를 입력할 수 있는 새로운 페이지(AddressDetailPage)를 추가했습니다.
  • Zustand 스토어를 통한 상태 관리 개선: 주소 선택 및 인화 옵션 페이지에서 usePrintOrderStore와 새로 추가된 useAddressIdStore를 활용하여 상태 동기화 및 영속성을 강화했습니다. 특히 뒤로가기/새로고침 시 선택된 옵션과 주소 정보가 유지되도록 개선되었습니다.
  • 실시간 가격 견적 및 UI 피드백 강화: 인화 옵션 선택 시 서버로부터 실시간으로 가격 견적을 받아 totalPrice를 업데이트하고, 견적 계산 중에는 UI에 '계산 중...' 메시지를 표시하여 사용자에게 명확한 피드백을 제공합니다.
  • CTA 버튼 및 드롭박스 UI/UX 개선: CTA 버튼에 gap-2 스타일을 추가하여 요소 간 간격을 조정하고, 드롭박스 옵션 선택 시 e.preventDefault()e.stopPropagation()을 적용하여 불필요한 이벤트 전파를 방지했습니다.
  • 배송/픽업 방식 선택 로직 개선: 배송/픽업 방식 선택 페이지에서 다음 단계로 진행 시, 기존 배송지 정보와 선택된 주소 ID를 초기화하여 새로운 주소 입력을 유도하고, 픽업 선택 시 인화 옵션을 초기화하도록 변경했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/components/common/CTA_Button.tsx
    • 기본 버튼 스타일에 gap-2 클래스를 추가하여 내부 요소 간 간격을 조정했습니다.
  • src/components/photoManage/DropBox.tsx
    • 불필요한 주석을 제거했습니다.
    • leftTextClass의 문자열 보간 방식을 수정했습니다.
    • 드롭다운 옵션의 onClick 핸들러에 e.preventDefault()e.stopPropagation()을 추가하여 부모 요소의 토글 동작을 방지했습니다.
  • src/pages/photoManage/AddressDetailPage.tsx
    • 새로운 상세 주소 입력 페이지를 추가했습니다.
    • Daum 주소 검색 기능을 통합하고, 주소명과 상세 주소 입력을 관리합니다.
    • usePrintOrderStoreuseCreateAddress 훅과 연동하여 주소 생성 및 저장을 처리합니다.
    • 폼 제출 유효성 검사 로직을 포함했습니다.
  • src/pages/photoManage/DetailInfoPage.tsx
    • 수신자 이름과 전화번호 상태를 usePrintOrderStoredeliveryAddress에서 초기화하도록 변경했습니다.
    • useEffect를 사용하여 로컬 상태를 스토어 업데이트와 동기화하도록 구현했습니다.
    • handlePhoneChangehandleNameChange 함수를 업데이트하여 스토어의 deliveryAddress를 직접 업데이트하도록 했습니다.
    • 인화 옵션 페이지로 이동 시 selectedOptions를 초기화하도록 추가했습니다.
  • src/pages/photoManage/PickUpMethodPage.tsx
    • selectedMethod 상태를 usePrintOrderStorereceiptMethod에서 초기화하여 선택 영속성을 확보했습니다.
    • useAddressIdStore를 통합하여 선택된 주소 ID를 관리하도록 했습니다.
    • handleNext 함수를 수정하여 다음 단계 진행 시 deliveryAddressselectedAddressId를 초기화하도록 했습니다.
    • 픽업 방식을 선택할 경우 selectedOptions를 초기화하도록 추가했습니다.
  • src/pages/photoManage/PrintOptionPage.tsx
    • PrintQuoteResponse 타입 임포트를 제거했습니다.
    • 드롭다운 옵션을 쉽게 찾기 위한 findOption 헬퍼 함수를 추가했습니다.
    • 초기 selection 상태를 스토어의 selectedOptions를 사용하여 설정하여 페이지 이동 시 상태 복원을 가능하게 했습니다.
    • 가격 견적 로직을 리팩토링하여 isQuoting 상태를 사용하고 totalPriceusePrintOrderStore에 직접 업데이트하도록 했습니다.
    • 가격 견적을 위한 useEffect 훅을 개선하여 동시 요청을 처리하고 '계산 중...' UI 피드백을 제공하도록 했습니다.
    • canPay 조건을 필수 옵션 선택 여부, 견적 상태, 총 금액, 제출 상태를 고려하여 정확하게 조정했습니다.
    • 총 금액 표시 시 '계산 중...' 메시지를 추가하고, CTA 버튼의 텍스트와 비활성화 상태를 견적 상태에 따라 동적으로 변경했습니다.
    • 옵션 선택 시 즉시 영속성 처리가 되도록 handleSubmit에서 setSelectedOptions 호출을 제거했습니다.
  • src/router/Router.tsx
    • AddressDetailPage에 대한 새 라우트를 /photoManage/address-detail 경로에 추가했습니다.
  • src/store/useAddressId.store.ts
    • 선택된 주소 ID를 관리하기 위한 새로운 Zustand 스토어(useAddressIdStore)를 생성하여 모듈성과 상태 관리를 개선했습니다.
Activity
  • jeonbinggu님이 현상관리 페이지의 2차 QA 반영을 위한 PR을 생성했습니다.
  • PM-043 배송지 상세 정보 입력 기능이 구현되었습니다.
  • PM-050 CTA 확인(고정) 관련 변경사항이 적용되었습니다.
  • PM-051 상태 저장을 위한 스토어(Zustand)가 도입되었습니다.
  • PM-060 복사 토스트의 상하 패딩이 조정되었습니다.
  • 팝업 외부 클릭 시 비활성화되는 기능이 추가되었습니다.
  • PM-060 뒤로가기 시 상태 초기화(clean) 로직이 적용되었습니다.
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

현상 관리 플로우의 상태 관리를 Zustand 스토어를 사용하도록 리팩토링하여 페이지 간 상태를 유지하는 훌륭한 개선이 이루어졌습니다. 이를 통해 뒤로가기나 새로고침 시에도 사용자 경험이 향상되었습니다. 주소 선택 및 가격 견적 로직 또한 더 견고해졌습니다. 리뷰 중 몇 군데에서 window.setTimeout(..., 0) 패턴이 반복적으로 사용되는 것을 확인했는데, 코드의 명확성과 예측 가능성을 높이기 위해 이를 단순화할 수 있는 부분을 제안했습니다. 또한 타입 정의를 개선하여 더 타입-세이프하고 명확한 코드를 작성할 수 있는 부분에 대한 제안도 포함했습니다. 전반적으로 이 기능의 아키텍처를 개선한 훌륭한 작업입니다.

Comment on lines +28 to +34
useEffect(() => {
const id = window.setTimeout(() => {
setAddressDetail(deliveryAddress?.addressDetail ?? "");
}, 0);

return () => window.clearTimeout(id);
}, [deliveryAddress?.addressDetail]);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

여기서 window.setTimeout(..., 0)을 사용하는 것은 불필요해 보입니다. useEffect는 렌더링 후에 실행되며, React가 상태 업데이트를 배치(batch) 처리합니다. setTimeout을 사용하면 컴포넌트의 동작을 예측하기 어렵게 만들고 디버깅을 복잡하게 할 수 있습니다. useEffect 내에서 직접 setAddressDetail을 호출하는 것이 더 명확합니다.

  useEffect(() => {
    setAddressDetail(deliveryAddress?.addressDetail ?? "");
  }, [deliveryAddress?.addressDetail]);

Comment on lines +48 to +54
setDeliveryAddress({
recipientName: "", // store 타입 유지용(지금 플로우에선 안 씀)
phone: "", // store 타입 유지용(지금 플로우에선 안 씀)
zipcode: data.zipcode,
address: data.address,
addressDetail, // 현재 입력중 상세주소 유지
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

recipientNamephone에 빈 문자열을 할당하여 타입 호환성을 맞추는 것보다, DeliveryAddressRequest 타입 자체에서 해당 속성들을 선택적(optional)으로 만드는 것이 더 나은 접근 방식일 수 있습니다. 이렇게 하면 아직 값이 없는 상태를 더 명확하게 표현할 수 있고, 불필요한 플레이스홀더 값 사용을 피할 수 있습니다. 예를 들어, 타입을 다음과 같이 수정하는 것을 고려해볼 수 있습니다.

// In src/types/photomanage/printOrder.ts
export interface DeliveryAddressRequest {
  recipientName?: string;
  phone?: string;
  zipcode: string;
  address: string;
  addressDetail?: string;
}

Comment on lines +23 to +30
useEffect(() => {
const id = window.setTimeout(() => {
setRecipientName(deliveryAddress?.recipientName ?? "");
setPhone(deliveryAddress?.phone ?? "");
}, 0);

return () => window.clearTimeout(id);
}, [deliveryAddress?.recipientName, deliveryAddress?.phone]);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

window.setTimeout(..., 0)을 사용하여 상태 업데이트를 스케줄링할 필요가 없어 보입니다. useEffect 내에서 직접 상태 설정 함수(setRecipientName, setPhone)를 호출하는 것이 코드를 더 간단하고 예측 가능하게 만듭니다.

  useEffect(() => {
    setRecipientName(deliveryAddress?.recipientName ?? "");
    setPhone(deliveryAddress?.phone ?? "");
  }, [deliveryAddress?.recipientName, deliveryAddress?.phone]);

Comment on lines +128 to +150
useEffect(() => {
if (!categories.length) return;

const id = window.setTimeout(() => {
setSelection({
FILM: findOption(categories, "FILM", selectedOptions.filmType),
PRINT_METHOD: findOption(
categories,
"PRINT_METHOD",
selectedOptions.printMethod,
),
PAPER: findOption(categories, "PAPER", selectedOptions.paperType),
SIZE: findOption(categories, "SIZE", selectedOptions.size),
PRINT_TYPE: findOption(
categories,
"PRINT_TYPE",
selectedOptions.frameType,
),
});
}, 0);

return () => window.clearTimeout(id);
}, [categories, selectedOptions]);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

useEffect에서도 window.setTimeout(..., 0)을 사용하고 있는데, 특별한 이유가 없다면 불필요합니다. setTimeout 없이 직접 setSelection을 호출해도 동일하게 동작하며 코드가 더 간결해집니다.

  //뒤로가기/새로고침 복원: store.selectedOptions(코드) -> DropDownOption
  useEffect(() => {
    if (!categories.length) return;

    setSelection({
      FILM: findOption(categories, "FILM", selectedOptions.filmType),
      PRINT_METHOD: findOption(
        categories,
        "PRINT_METHOD",
        selectedOptions.printMethod,
      ),
      PAPER: findOption(categories, "PAPER", selectedOptions.paperType),
      SIZE: findOption(categories, "SIZE", selectedOptions.size),
      PRINT_TYPE: findOption(
        categories,
        "PRINT_TYPE",
        selectedOptions.frameType,
      ),
    });
  }, [categories, selectedOptions]);

Comment on lines +215 to +236
const t = window.setTimeout(() => {
setIsQuoting(true);
}, 0);

quotePrintPrice(request)
.then((res) => {
if (quoteRequestId.current === id) setQuote(res.data);
if (quoteRequestId.current !== id) return;

//store에 바로 반영
setTotalPrice(res.data.totalAmount ?? 0);
})
.catch((err) => {
console.error("견적 조회 실패:", err);
if (quoteRequestId.current !== id) return;
setTotalPrice(0);
})
.finally(() => {
if (quoteRequestId.current !== id) return;
window.setTimeout(() => setIsQuoting(false), 0);
});
}, [buildQuoteRequest]);

// 사이즈·인화유형 미선택 시 견적 표시하지 않음
const displayedQuote = selection.SIZE && selection.PRINT_TYPE ? quote : null;
return () => window.clearTimeout(t);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

isQuoting 상태를 설정하기 위해 window.setTimeout을 사용하는 것은 불필요합니다. useEffect의 시작 부분에서 setIsQuoting(true)를, finally 블록에서 setIsQuoting(false)를 직접 호출하는 것이 더 간단하고 명확한 방법입니다.

    if (!request) return;

    const id = ++quoteRequestId.current;
    setIsQuoting(true);

    quotePrintPrice(request)
      .then((res) => {
        if (quoteRequestId.current !== id) return;

        //store에 바로 반영
        setTotalPrice(res.data.totalAmount ?? 0);
      })
      .catch((err) => {
        console.error("견적 조회 실패:", err);
        if (quoteRequestId.current !== id) return;
        setTotalPrice(0);
      })
      .finally(() => {
        if (quoteRequestId.current !== id) return;
        setIsQuoting(false);
      });

Copy link
Contributor

Choose a reason for hiding this comment

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

SHOULD: 해당 부분 race condition이 생길 수 있을거 같아 추후 확인 부탁드려요!

Copy link
Contributor

@MlNTYS MlNTYS left a comment

Choose a reason for hiding this comment

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

PRAISE: comment한 부분만 빼면 전부 잘 해주셨습니다!

@jeonbinggu jeonbinggu merged commit 03342ef into develop Feb 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

design UI, 디자인 변경 feature 새 기능 추가 refactor 내부 구조 개선(가독성,확장성,유지보수성)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] 현상관리 2차 QA 반영 (PM-031 ~ PM-060)

3 participants