Skip to content

[Refactor/#114] 사장님 인증 관련 api 수정#115

Merged
skyblue1232 merged 4 commits into
developfrom
refactor/#114/owner-auth
May 17, 2026
Merged

[Refactor/#114] 사장님 인증 관련 api 수정#115
skyblue1232 merged 4 commits into
developfrom
refactor/#114/owner-auth

Conversation

@skyblue1232

@skyblue1232 skyblue1232 commented May 17, 2026

Copy link
Copy Markdown
Contributor

✅ 작업 내용

📝 Description

사장님 회원가입/등록 플로우의 인증, 주소 검색, 메인 화면 데이터 연동을 수정했습니다.

작업한 내용을 체크해주세요.

  • 회원가입 후 accessToken 저장 처리
  • 사업자 등록 API 인증 흐름 수정
  • 주소 검색을 Daum 우편번호 서비스로 변경
  • 주소 선택값을 상점 주소 입력창에 반영
  • 메인 화면 preview 데이터 연동 확인

🚀 설계 의도 및 개선점

회원가입 이후 인증 토큰이 저장되지 않아 발생하던 API 인증 문제를 수정했습니다.
주소 검색은 지도 기능이 필요하지 않아 Daum 우편번호 서비스 기반으로 변경했습니다.
메인 화면은 preview API 데이터를 기준으로 표시되도록 정리했습니다.

📸 스크린샷 (선택)

  • 주소 검색 화면
  • 사장님 메인 화면

📎 기타 참고사항

  • Kakao Maps Key 사용하지 않음
  • Daum 우편번호 스크립트 사용

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 회원가입 응답에 접근 토큰이 추가되어 더 빠른 로그인 처리가 가능합니다.
    • 주소 검색이 개선되어 더 안정적인 주소 입력 경험을 제공합니다.
  • 버그 수정

    • 정산 미리보기 데이터 누락 시 발생하던 오류가 해결되었습니다.
    • 가게명 표시 시 공백 처리 및 기본값 표시 기능이 개선되었습니다.

Review Change Stack

@skyblue1232 skyblue1232 self-assigned this May 17, 2026
@skyblue1232 skyblue1232 added chore 자잘한 수정 api 서버 - 클라이언트 통신 labels May 17, 2026
@vercel

vercel Bot commented May 17, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
compasser-customer Ready Ready Preview, Comment May 17, 2026 8:36am
compasser-owner Ready Ready Preview, Comment May 17, 2026 8:36am

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@skyblue1232 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 51 minutes and 23 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f42e6d95-17c9-45d3-86e5-8096bb83f9bf

📥 Commits

Reviewing files that changed from the base of the PR and between 80a90fb and 6ef1995.

📒 Files selected for processing (1)
  • apps/owner/src/app/(tabs)/mypage/store-account/page.tsx
📝 Walkthrough

Walkthrough

이 PR은 주소 검색 기능을 Kakao 기반 검색에서 Daum Postcode 팝업 기반 단일 흐름으로 전환하고, 회원가입 성공 시 accessToken을 로컬 스토리지에 저장하며, 정산 데이터 및 UI 표시 값에 기본값 처리를 추가합니다.

Changes

주소 검색 및 회원가입 통합

Layer / File(s) Summary
주소 검색 타입 계약 및 Daum Postcode 정의
apps/owner/src/app/signup/register/_types/address-search.ts, packages/api/src/models/auth.ts
AddressSearchItem의 필드를 지리 좌표 기반에서 roadAddress, lotNumberAddress, zonecode 기반으로 변경합니다. DaumPostcodeData 인터페이스를 추가하여 Daum Postcode 콜백 응답 구조를 정의하고, Window 전역 타입에 생성자 옵션 타입을 선언합니다. JoinRespDTOaccessToken 필드를 추가합니다.
Daum Postcode 스크립트 로드 및 오픈 API
apps/owner/src/app/signup/register/_apis/loadDaumPostcode.ts, apps/owner/src/app/signup/register/_apis/openDaumPostcode.ts
loadDaumPostcode()는 CDN에서 Daum Postcode 스크립트를 동적으로 로드하고, 이미 로드된 경우 또는 기존 스크립트 로드 이벤트를 재사용합니다. openDaumPostcode()는 로드를 보장한 뒤 팝업을 열고 사용자 선택 결과를 AddressSearchItem으로 변환하여 반환합니다.
주소 검색 UI 전환
apps/owner/src/app/signup/register/_components/AddressSearchBottomSheet.tsx, apps/owner/src/app/signup/register/_apis/searchAddressBykakao.ts
AddressSearchBottomSheet를 Kakao 검색 입력 기반 UI에서 Daum Postcode 오픈 버튼 기반 UI로 전환합니다. 키워드 입력 및 검색 결과 리스트를 제거하고, open 시 1회 자동 실행되는 useEffect와 수동 재오픈 버튼을 추가합니다. searchAddressByKakao 함수 전체를 제거합니다.
회원가입 accessToken 저장
apps/owner/src/app/signup/page.tsx
signupMutationonSuccess 콜백에서 응답 데이터의 accessToken을 추출하여 localStorage의 ownerAccessToken 키에 저장합니다. 저장 후 기존과 동일하게 setSignupCompleted(email)/signup/business 라우팅을 수행합니다.
정산 및 UI 데이터 기본값 처리
apps/owner/src/app/(tabs)/main/page.tsx, apps/owner/src/app/(tabs)/main/_components/CafeIntro.tsx, apps/customer/next-env.d.ts
예약 데이터 매핑 시 totalPrice?? 0 기본값을 추가합니다. TodayRewards에 전달되는 보상 지표(couponUsedCount, stampSavedCount)에도 기본값을 추가합니다. CafeIntrocafeName Props 타입을 string | null | undefined로 변경하고, "등록된 가게" 기본값 및 공백 트림 처리를 추가합니다. next-env.d.ts의 routes 타입 경로를 업데이트합니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • CUK-Compasser/FE#111: 이 PR이 새로 추가한 주소 검색 UI(AddressSearchBottomSheet.tsx)와 searchAddressByKakao 함수를 기반으로, 현재 PR이 Daum Postcode 기반으로 재구성합니다.
  • CUK-Compasser/FE#64: 현재 PR의 회원가입 accessToken 저장 및 /signup/business 라우팅 로직이 이 PR에서 만든 비즈니스 등록 화면 흐름과 직접 연결됩니다.
  • CUK-Compasser/FE#96: 이 PR의 settlementPreview 기반 데이터 렌더링이 현재 PR의 reservation.totalPrice ?? 0 기본값 처리를 통해 안정화됩니다.

🐰 주소 검색이 새 집으로 이사했네,
Daum의 팝업은 쏙 튀어나오고,
토큰도 안전한 localStorage 속으로,
데이터 기본값이 낙숫소처럼 떨어지니,
이제 가게도 주소도 든든하다 굴러굴러 🍀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주소 검색 Daum 서비스로 변경, 회원가입 accessToken 저장, API 인증 흐름 개선 등 여러 주요 변경사항을 모두 다루고 있으며, 특히 '사장님 인증 관련 api 수정'은 주요 변경의 핵심을 잘 반영하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/#114/owner-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/customer/next-env.d.ts`:
- Line 3: The import line `import "./.next/dev/types/routes.d.ts";` hardcodes a
dev-only path and should be removed; add the next-env.d.ts file to .gitignore,
delete the committed next-env.d.ts from the repo so Next.js can auto-generate it
per its docs, and update tsconfig.json to include a wildcard like
`.next/types/**/*.d.ts` (or `.next/types/**/*.ts`) so both dev and production/CI
generated types are picked up; ensure no manual edits remain to next-env.d.ts.

In `@apps/owner/src/app/signup/page.tsx`:
- Around line 62-66: onSuccess handler currently writes res.data.accessToken to
localStorage without validation; add a guard in the onSuccess function to verify
accessToken is a non-empty string (e.g., check typeof accessToken === "string"
&& accessToken.trim() !== ""), only call
localStorage.setItem("ownerAccessToken", accessToken) when valid, and otherwise
handle the failure branch (e.g., do not store, call the error/fallback flow or
show a notification). Ensure you modify the onSuccess callback where accessToken
is read and stored so invalid/undefined/empty tokens are not persisted.

In `@apps/owner/src/app/signup/register/_apis/loadDaumPostcode.ts`:
- Around line 17-33: When an existingScript is found the code only attaches
load/error listeners which can leave the Promise pending if the script has
already finished loading but window.daum?.Postcode is not present; update the
existingScript branch in loadDaumPostcode to first check synchronously whether
window.daum?.Postcode exists and resolve immediately if so, otherwise detect a
completed script state (e.g., existingScript.readyState === "complete" or a
custom data attribute) and reject with a clear error instead of waiting, and
only attach addEventListener("load"/"error") as a fallback for truly pending
scripts so resolve/reject always occurs.

In `@apps/owner/src/app/signup/register/_apis/openDaumPostcode.ts`:
- Around line 16-29: The Postcode popup currently only handles oncomplete and
never rejects if the user closes the popup, so add an onclose handler on the new
window.daum.Postcode instance that inspects the close state and rejects the
Promise when state === "FORCE_CLOSE"; to avoid racing with
oncomplete/COMPLETE_CLOSE use a local settled boolean (e.g., settled = false)
that you set to true inside the oncomplete handler before calling resolve and
check in onclose to only call reject when settled is false, and ensure both
resolve and reject clear/set the settled flag so the Promise is settled exactly
once.

In `@apps/owner/src/app/signup/register/_types/address-search.ts`:
- Around line 21-24: Postcode 타입의 onclose 시그니처가 Daum Postcode 공식 API와 다르므로
수정하세요: 현재 Postcode constructor options includes oncomplete and onclose but
onclose is declared as onclose?: () => void; — change it to accept the close
state parameter (e.g., onclose?: (state: "COMPLETE_CLOSE" | "FORCE_CLOSE") =>
void) so it matches DaumPostcodeData usage and lets callers distinguish user
cancel vs complete; also update any callers of Postcode/new Postcode(...) (and
related dialog close handling) to pass/handle the state and ensure the dialog
close path is implemented where Postcode is used.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 87c424a6-394f-4b62-9a6e-4847a8cb07ba

📥 Commits

Reviewing files that changed from the base of the PR and between 7ef7cd0 and 80a90fb.

📒 Files selected for processing (10)
  • apps/customer/next-env.d.ts
  • apps/owner/src/app/(tabs)/main/_components/CafeIntro.tsx
  • apps/owner/src/app/(tabs)/main/page.tsx
  • apps/owner/src/app/signup/page.tsx
  • apps/owner/src/app/signup/register/_apis/loadDaumPostcode.ts
  • apps/owner/src/app/signup/register/_apis/openDaumPostcode.ts
  • apps/owner/src/app/signup/register/_apis/searchAddressBykakao.ts
  • apps/owner/src/app/signup/register/_components/AddressSearchBottomSheet.tsx
  • apps/owner/src/app/signup/register/_types/address-search.ts
  • packages/api/src/models/auth.ts
💤 Files with no reviewable changes (1)
  • apps/owner/src/app/signup/register/_apis/searchAddressBykakao.ts

/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/dev/types/routes.d.ts";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Next.js 16 기준으로 next-env.d.ts에서 typed routes 참조 경로는 "./.next/types/routes.d.ts"와 "./.next/dev/types/routes.d.ts" 중 무엇이 공식적으로 권장되나요? dev/prod(typecheck, CI)별 생성 경로 차이도 함께 확인해주세요.

💡 Result:

Next.js 16에서 next-env.d.ts 파일 내의 typed routes 참조 경로는 사용자가 직접 수정하거나 선택하는 것이 아니며, Next.js가 실행 환경(dev 또는 build)에 따라 자동으로 관리하는 영역입니다 [1]. 공식적으로 권장되는 특정 경로가 하나로 고정되어 있는 것이 아니라, Next.js의 내부 동작 방식에 따라 다음과 같이 생성됩니다 [1]. 1. 생성 경로 차이: - 개발 환경(next dev):.next/dev/types/routes.d.ts 경로를 참조하도록 생성됩니다 [1]. - 빌드 환경(next build):.next/types/routes.d.ts 경로를 참조하도록 생성됩니다 [1]. 이는 Next.js의 isolatedDevBuild 옵션 도입으로 인해 개발 시의 타입 생성 경로와 빌드 시의 경로가 분리되었기 때문입니다 [1]. 이로 인해 next-env.d.ts 파일 내의 참조 경로가 실행 환경에 따라 토글(toggle)되는 현상이 발생할 수 있으며, 이는 의도된 동작입니다 [1]. 2. 권장 사항: - next-env.d.ts 파일은 Next.js가 자동으로 생성하고 관리하는 파일이므로, 개발자가 직접 이 파일의 참조 경로를 수정해서는 안 됩니다 [2][3]. - 해당 파일은.gitignore에 추가하여 버전 관리에서 제외하는 것이 공식적인 권장 사항입니다 [2][3]. - 프로젝트의 tsconfig.json 파일 내 include 배열에는.next/types//*.ts와 같이 와일드카드를 사용하여 포함시키는 것이 권장됩니다 [2][3]. 이렇게 설정하면 개발 환경과 빌드 환경 모두에서 타입 정의를 올바르게 인식할 수 있습니다 [2][3]. 결론적으로, 특정 경로를 선택하는 것이 아니라 Next.js가 자동으로 생성하는 경로를 그대로 두되, tsconfig.json 설정을 통해 모든 환경에서 타입이 정상적으로 참조되도록 구성하는 것이 올바른 방법입니다 [1][2][3].

Citations:


🏁 Script executed:

#!/bin/bash
# 리포 내 next-env.d.ts들의 routes 참조 경로를 비교해 일관성 확인
fd -a "next-env.d.ts" | while read -r f; do
  echo "== $f =="
  rg -n 'routes\.d\.ts|next-env|reference types="next"' "$f"
  echo
done

Repository: CUK-Compasser/FE

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if next-env.d.ts exists in the repository
find . -name "next-env.d.ts" -type f

# Check .gitignore for next-env.d.ts rules
echo "=== .gitignore content ==="
cat .gitignore 2>/dev/null | grep -E "next-env|\.next" || echo "No .next-related rules found"

# Check if apps/customer/next-env.d.ts exists with line count
if [ -f "apps/customer/next-env.d.ts" ]; then
  echo "=== apps/customer/next-env.d.ts exists ==="
  cat -n "apps/customer/next-env.d.ts"
fi

Repository: CUK-Compasser/FE

Length of output: 509


next-env.d.ts의 dev 전용 경로 고정은 빌드/CI 환경에서 타입을 찾지 못해 빌드 실패를 초래합니다.

Line 3에서 ./.next/dev/types/routes.d.ts를 하드코딩하면 개발 환경에서는 작동하지만, 프로덕션 빌드 및 CI 환경에서는 ./.next/types/routes.d.ts를 참조하므로 타입 파일을 찾지 못합니다. 더 근본적으로, Next.js 공식 문서(파일의 5-6줄 주석 참조)에 따르면 next-env.d.ts는 Next.js가 자동으로 생성·관리하는 파일이며 수동 편집 금지입니다. 또한 이 파일은 .gitignore에 추가되어야 하며, tsconfig.json에 .next/types/**/*.ts 와일드카드를 사용하여 양쪽 환경을 모두 처리해야 합니다.

  • apps/customer/next-env.d.ts를 .gitignore에 추가
  • 이 파일을 저장소에서 제거하고 각 개발자/CI 환경에서 자동 생성되도록 구성
  • 필요시 tsconfig.json 설정 검토
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/customer/next-env.d.ts` at line 3, The import line `import
"./.next/dev/types/routes.d.ts";` hardcodes a dev-only path and should be
removed; add the next-env.d.ts file to .gitignore, delete the committed
next-env.d.ts from the repo so Next.js can auto-generate it per its docs, and
update tsconfig.json to include a wildcard like `.next/types/**/*.d.ts` (or
`.next/types/**/*.ts`) so both dev and production/CI generated types are picked
up; ensure no manual edits remain to next-env.d.ts.

Comment on lines +62 to +66
onSuccess: (res) => {
const accessToken = res.data.accessToken;

localStorage.setItem("ownerAccessToken", accessToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

accessToken 유효성 검증 후 저장하도록 가드가 필요합니다.

응답이 비정상일 때 undefined/빈 문자열이 저장되면 이후 인증 흐름이 깨질 수 있습니다. 저장 전에 검증하고 실패 처리를 분기해 주세요.

🔧 제안 수정안
         onSuccess: (res) => {
-          const accessToken = res.data.accessToken;
-
-          localStorage.setItem("ownerAccessToken", accessToken);
+          const accessToken = res.data?.accessToken?.trim();
+          if (!accessToken) {
+            console.error("회원가입 응답에 accessToken이 없습니다.");
+            return;
+          }
+          localStorage.setItem("ownerAccessToken", accessToken);

           setSignupCompleted(email);
           router.push("/signup/business");
         },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onSuccess: (res) => {
const accessToken = res.data.accessToken;
localStorage.setItem("ownerAccessToken", accessToken);
onSuccess: (res) => {
const accessToken = res.data?.accessToken?.trim();
if (!accessToken) {
console.error("회원가입 응답에 accessToken이 없습니다.");
return;
}
localStorage.setItem("ownerAccessToken", accessToken);
setSignupCompleted(email);
router.push("/signup/business");
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/owner/src/app/signup/page.tsx` around lines 62 - 66, onSuccess handler
currently writes res.data.accessToken to localStorage without validation; add a
guard in the onSuccess function to verify accessToken is a non-empty string
(e.g., check typeof accessToken === "string" && accessToken.trim() !== ""), only
call localStorage.setItem("ownerAccessToken", accessToken) when valid, and
otherwise handle the failure branch (e.g., do not store, call the error/fallback
flow or show a notification). Ensure you modify the onSuccess callback where
accessToken is read and stored so invalid/undefined/empty tokens are not
persisted.

Comment on lines +17 to +33
if (existingScript) {
existingScript.addEventListener(
"load",
() => {
if (window.daum?.Postcode) resolve();
else reject(new Error("Daum Postcode is not loaded."));
},
{ once: true },
);

existingScript.addEventListener(
"error",
() => reject(new Error("Failed to load Daum Postcode script.")),
{ once: true },
);

return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

기존 스크립트 분기에서 Promise가 영구 대기 상태가 될 수 있습니다.

이미 로드가 끝난 스크립트인데 window.daum?.Postcode가 없는 경우, load/error가 다시 오지 않아 resolve/reject가 되지 않을 수 있습니다.

🔧 제안 수정안
     if (existingScript) {
+      const timeoutId = window.setTimeout(() => {
+        reject(new Error("Timed out while waiting for Daum Postcode script."));
+      }, 10000);
+
+      const cleanup = () => window.clearTimeout(timeoutId);
+
       existingScript.addEventListener(
         "load",
         () => {
+          cleanup();
           if (window.daum?.Postcode) resolve();
           else reject(new Error("Daum Postcode is not loaded."));
         },
         { once: true },
       );

       existingScript.addEventListener(
         "error",
-        () => reject(new Error("Failed to load Daum Postcode script.")),
+        () => {
+          cleanup();
+          reject(new Error("Failed to load Daum Postcode script."));
+        },
         { once: true },
       );

       return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/owner/src/app/signup/register/_apis/loadDaumPostcode.ts` around lines 17
- 33, When an existingScript is found the code only attaches load/error
listeners which can leave the Promise pending if the script has already finished
loading but window.daum?.Postcode is not present; update the existingScript
branch in loadDaumPostcode to first check synchronously whether
window.daum?.Postcode exists and resolve immediately if so, otherwise detect a
completed script state (e.g., existingScript.readyState === "complete" or a
custom data attribute) and reject with a clear error instead of waiting, and
only attach addEventListener("load"/"error") as a fallback for truly pending
scripts so resolve/reject always occurs.

Comment on lines +16 to +29
new window.daum.Postcode({
oncomplete: (data: DaumPostcodeData) => {
const roadAddress = data.roadAddress || data.address || "";
const lotNumberAddress = data.jibunAddress || "";

resolve({
id: `${data.zonecode}-${roadAddress || lotNumberAddress}`,
label: roadAddress || lotNumberAddress,
roadAddress,
lotNumberAddress,
zonecode: data.zonecode,
});
},
}).open();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Daum Postcode onclose 콜백이 oncomplete 이후에도 호출되는지, 그리고 FORCE_CLOSE/COMPLETE_CLOSE 의미 확인

💡 Result:

Daum 우편번호 서비스의 onclose 콜백은 oncomplete 콜백이 실행된 후에도 호출될 수 있습니다 [1][2]. onclose 콜백은 우편번호 검색창이 닫힐 때 호출되며, 이때 전달되는 state 인자를 통해 닫힌 이유를 구분할 수 있습니다 [2]. FORCE_CLOSE와 COMPLETE_CLOSE의 의미는 다음과 같습니다: 1. COMPLETE_CLOSE: 사용자가 검색 결과 항목을 선택하여 주소 정보가 전달된 후, 우편번호 서비스가 자동으로 닫히는 상태를 의미합니다 [2]. 즉, oncomplete 콜백이 정상적으로 실행 완료된 후에 이 상태로 onclose가 호출됩니다 [1][2]. 2. FORCE_CLOSE: 사용자가 검색 결과 항목을 선택하지 않고, 검색창의 닫기 버튼을 누르거나 외부 영역을 클릭하는 등 강제로 검색창을 닫았을 때 발생하는 상태입니다 [2]. 따라서 oncomplete는 주소 선택 시점에 실행되고, 이후 검색창이 닫히면서 onclose가 호출되는 흐름을 가집니다 [1][2]. 만약 oncomplete 내부에서 페이지 이동(location.href 변경 등)을 수행하면, 페이지가 초기화되어 onclose 콜백이 정상적으로 실행되지 않을 수 있으므로 주의가 필요합니다 [1].

Citations:


주소 선택 없이 팝업을 닫으면 Promise가 resolve/reject 되지 않습니다.

사용자가 주소를 선택하지 않고 닫기 버튼이나 외부 영역을 클릭하면 oncloseFORCE_CLOSE 상태로 호출되지만, 현재 코드는 이를 처리하지 않아 호출 측의 로딩 상태가 풀리지 않습니다. onclose에서 FORCE_CLOSE 상태를 감지하여 Promise를 reject해 주세요. (주소 선택 후 자동으로 닫히는 COMPLETE_CLOSE 상태는 oncomplete 이후에 호출되므로 settled 플래그로 구분하여 처리하면 됩니다.)

🔧 제안 수정안
   return new Promise((resolve, reject) => {
     if (!window.daum?.Postcode) {
       reject(new Error("Daum Postcode is not loaded."));
       return;
     }

+    let settled = false;
+
     new window.daum.Postcode({
       oncomplete: (data: DaumPostcodeData) => {
+        settled = true;
         const roadAddress = data.roadAddress || data.address || "";
         const lotNumberAddress = data.jibunAddress || "";

         resolve({
           id: `${data.zonecode}-${roadAddress || lotNumberAddress}`,
           label: roadAddress || lotNumberAddress,
           roadAddress,
           lotNumberAddress,
           zonecode: data.zonecode,
         });
       },
+      onclose: (state) => {
+        if (!settled && state === "FORCE_CLOSE") {
+          reject(new Error("사용자가 주소 검색을 취소했습니다."));
+        }
+      },
     }).open();
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/owner/src/app/signup/register/_apis/openDaumPostcode.ts` around lines 16
- 29, The Postcode popup currently only handles oncomplete and never rejects if
the user closes the popup, so add an onclose handler on the new
window.daum.Postcode instance that inspects the close state and rejects the
Promise when state === "FORCE_CLOSE"; to avoid racing with
oncomplete/COMPLETE_CLOSE use a local settled boolean (e.g., settled = false)
that you set to true inside the oncomplete handler before calling resolve and
check in onclose to only call reject when settled is false, and ensure both
resolve and reject clear/set the settled flag so the Promise is settled exactly
once.

Comment on lines +21 to +24
Postcode: new (options: {
oncomplete: (data: DaumPostcodeData) => void;
onclose?: () => void;
}) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Daum 우편번호 서비스(Postcode v2) onclose 콜백 시그니처와 state 값(COMPLETE_CLOSE/FORCE_CLOSE) 공식 문서 확인

💡 Result:

Daum 우편번호 서비스(Postcode v2)의 onclose 콜백 함수는 우편번호 찾기 화면이 닫힐 때 호출되며, 화면이 닫힌 방식에 대한 상태 정보를 인자로 전달받습니다 [1][2]. 콜백 시그니처: onclose: function(state) {... } state 인자 값 및 설명: 1. FORCE_CLOSE: 사용자가 브라우저의 닫기 버튼 등을 통해 팝업창을 직접 닫았을 경우 발생합니다 [1][3]. (레이어 모드에서는 발생하지 않습니다 [1].) 2. COMPLETE_CLOSE: 사용자가 검색 결과를 선택하여 화면이 닫혔을 경우 발생합니다 [1][3]. 이는 팝업 및 레이어 모드의 기본 동작이며, oncomplete 콜백 함수가 실행 완료된 후에 호출됩니다 [1][4][2]. 참고 사항: - 레이어 모드(embed 함수 사용)에서는 검색 결과를 선택하여 닫히는 경우에만 onclose 콜백이 실행됩니다 [1]. - oncomplete 콜백 함수가 실행되는 도중 오류가 발생하거나, autoClose 옵션을 false로 설정한 경우에는 자동으로 닫히지 않을 수 있습니다 [5].

Citations:


🏁 Script executed:

cd apps/owner/src/app/signup/register/_types && cat -n address-search.ts | head -30

Repository: CUK-Compasser/FE

Length of output: 808


🏁 Script executed:

rg "onclose" --type ts --type tsx -B 2 -A 2

Repository: CUK-Compasser/FE

Length of output: 88


🏁 Script executed:

rg "onclose" -B 2 -A 2

Repository: CUK-Compasser/FE

Length of output: 516


🏁 Script executed:

rg "daum\.Postcode|window\.daum" -B 3 -A 3

Repository: CUK-Compasser/FE

Length of output: 2951


🏁 Script executed:

cat -n apps/owner/src/app/signup/register/_apis/openDaumPostcode.ts

Repository: CUK-Compasser/FE

Length of output: 1151


onclose 콜백 시그니처를 Daum Postcode 공식 API와 일치시켜 주세요.

현재 타입 선언에서 onclose?: () => void;로 정의되어 있으나, 공식 Daum Postcode API에서는 onclose(state)"COMPLETE_CLOSE" 또는 "FORCE_CLOSE" 상태를 인자로 전달받습니다. 이 타입 선언으로는 사용자 취소 시나리오를 구분하여 처리할 수 없으며, 현재 구현에서도 다이얼로그 닫힘 처리가 누락되어 있습니다.

🔧 제안 수정안
       Postcode: new (options: {
         oncomplete: (data: DaumPostcodeData) => void;
-        onclose?: () => void;
+        onclose?: (state: "COMPLETE_CLOSE" | "FORCE_CLOSE") => void;
       }) => {
         open: () => void;
       };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Postcode: new (options: {
oncomplete: (data: DaumPostcodeData) => void;
onclose?: () => void;
}) => {
Postcode: new (options: {
oncomplete: (data: DaumPostcodeData) => void;
onclose?: (state: "COMPLETE_CLOSE" | "FORCE_CLOSE") => void;
}) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/owner/src/app/signup/register/_types/address-search.ts` around lines 21
- 24, Postcode 타입의 onclose 시그니처가 Daum Postcode 공식 API와 다르므로 수정하세요: 현재 Postcode
constructor options includes oncomplete and onclose but onclose is declared as
onclose?: () => void; — change it to accept the close state parameter (e.g.,
onclose?: (state: "COMPLETE_CLOSE" | "FORCE_CLOSE") => void) so it matches
DaumPostcodeData usage and lets callers distinguish user cancel vs complete;
also update any callers of Postcode/new Postcode(...) (and related dialog close
handling) to pass/handle the state and ensure the dialog close path is
implemented where Postcode is used.

@skyblue1232 skyblue1232 merged commit 32d74c3 into develop May 17, 2026
5 checks passed
@skyblue1232 skyblue1232 linked an issue May 17, 2026 that may be closed by this pull request
15 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api 서버 - 클라이언트 통신 chore 자잘한 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactor] 사업자 인증 관련 API 수정

1 participant