[FEATURE] 팀피셜록 공유하기 모달 추가 - #168
Conversation
Walkthrough팀피셜록 공유 기능을 추가합니다. Kakao SDK 타입 및 스크립트, LinkShareModal 컴포넌트, UUID 기반 공유 모드 로직, 링크 공유 모달 오픈 흐름, 라우팅 리다이렉트, 헤더 및 UI 조정을 구현합니다. Changes팀피셜록 공유 기능 구현
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (1 warning, 2 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
src/app/(main)/page.tsxOops! Something went wrong! :( ESLint: 9.36.0 TypeError: Converting circular structure to JSON src/app/(main)/teampsylog/[uuid]/_components/KeywordListPage.tsxOops! Something went wrong! :( ESLint: 9.36.0 TypeError: Converting circular structure to JSON src/app/(main)/teampsylog/_components/CommentPage.tsxOops! Something went wrong! :( ESLint: 9.36.0 TypeError: Converting circular structure to JSON
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/app/(main)/teampsylog/_components/KeywordBar.tsx (1)
62-64:⚠️ Potential issue | 🟠 Major | ⚡ Quick win공유 링크 경로가 현재 라우팅 계약과 불일치합니다.
여기서 생성하는 URL이
/teampsylog/head/${uuid}인데, 현재 변경 흐름은/teampsylog/:uuid기준입니다. 이 상태면 공유 링크가 잘못 생성될 수 있습니다.수정 예시
- const url = uuid - ? `${window.location.origin}/teampsylog/head/${uuid}` - : `${window.location.origin}/teampsylog`; + const url = uuid + ? `${window.location.origin}/teampsylog/${uuid}` + : `${window.location.origin}/teampsylog`;🤖 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 `@src/app/`(main)/teampsylog/_components/KeywordBar.tsx around lines 62 - 64, The share URL generation in KeywordBar.tsx is using the wrong path segment; update the logic that constructs the url (the const named url using uuid and window.location.origin) to emit the route that matches the app routing contract (/teampsylog/:uuid) instead of `/teampsylog/head/${uuid}` so shared links point to `/teampsylog/{uuid}` when uuid exists and to `/teampsylog` when it does not.src/components/common/Header.tsx (1)
77-96:⚠️ Potential issue | 🟠 Major | ⚡ Quick win데스크톱에서 팀피셜록 링크까지 함께 비활성화되었습니다
Line 77-96 주석 범위에
/teampsylog링크도 포함되어 있어, 데스크톱 헤더에서 팀피셜록으로 직접 이동할 수 없습니다. 프로젝트 링크만 숨기는 의도였다면 주석 범위를 분리해 팀피셜록 링크는 유지해 주세요.제안 수정안
- {/* <Link - href="/project" - className={clsx( - 'hover:text-primary-900 px-3 transition-colors', - pathname.startsWith('/project') ? 'text-primary-900 body-3' : 'body-4 text-gray-900', - )} - > - 프로젝트 - </Link> - <Link + {/* <Link + href="/project" + className={clsx( + 'hover:text-primary-900 px-3 transition-colors', + pathname.startsWith('/project') ? 'text-primary-900 body-3' : 'body-4 text-gray-900', + )} + > + 프로젝트 + </Link> */} + <Link href="/teampsylog" className={clsx( 'hover:text-primary-900 px-3 transition-colors', pathname.startsWith('/teampsylog') ? 'text-primary-900 body-3' : 'body-4 text-gray-900', )} > 팀피셜록 - </Link> */} + </Link>🤖 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 `@src/components/common/Header.tsx` around lines 77 - 96, The commented block in Header.tsx currently wraps both Link components (href="/project" and href="/teampsylog"), unintentionally disabling the 팀피셜록 link; update the JSX so only the 프로젝트 Link (Link with href="/project") is commented/removed while leaving the 팀피셜록 Link (Link with href="/teampsylog") active — i.e., split the comment boundaries to exclude the href="/teampsylog" Link or uncomment that specific Link so the 팀피셜록 navigation appears in the desktop header.
🤖 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 `@src/app/`(main)/teampsylog/_components/KeywordBar.tsx:
- Around line 65-68: Wrap the navigator.clipboard.writeText(url) call in a
try/catch inside the KeywordBar component so a rejected promise doesn’t block
the sharing flow: call navigator.clipboard.writeText(url) inside try, and in
catch call addToast (or the existing toast helper) with a failure message and/or
fallback behavior, but always proceed to call openModal('linkShare') after the
attempt; reference the navigator.clipboard.writeText call, the
openModal('linkShare') invocation, and addToast (currently commented) when
implementing the change.
In `@src/components/common/KaKaoScript.tsx`:
- Around line 13-15: The Kakao SDK init call in KaKaoScript.tsx currently uses a
non-null assertion on process.env.NEXT_PUBLIC_KAKAO_JS_KEY and will throw if the
env var is missing; change the onLoad logic to first check that
process.env.NEXT_PUBLIC_KAKAO_JS_KEY (or a runtime variable passed into the
component) is a non-empty string before calling window.Kakao.init, skip/init
only when window.Kakao exists and !window.Kakao.isInitialized(), and optionally
emit a console.warn or processLogger message when the key is not set to make the
failure visible without breaking the app; reference window.Kakao and the init
call in KaKaoScript.tsx to locate the change.
In `@src/components/modal/LinkShareModal.tsx`:
- Line 28: The description string in LinkShareModal uses userName as-is, which
yields "null님..." when userName is null; update the description generation
(where description: `${userName}님이 협업 후기를 기다리고 있어요!` is set) to guard against
null/undefined by providing a fallback display name (e.g., use a default like
'사용자' or '누군가' or a localized fallback) or conditionally render an alternative
sentence when userName is missing so the UI never shows "null님".
- Around line 11-14: The getShareUrl function builds a share link that currently
points to /teampsylog/head/${uuid} which mismatches the agreed routing of
/teampsylog/:uuid; update getShareUrl to construct the URL using the UUID only
(i.e., change the path to /teampsylog/${uuid}), keep UUID extraction via
window.location.pathname.split('/').pop(), and ensure the returned value uses
window.location.origin combined with the corrected path so shared links hit the
proper route.
---
Outside diff comments:
In `@src/app/`(main)/teampsylog/_components/KeywordBar.tsx:
- Around line 62-64: The share URL generation in KeywordBar.tsx is using the
wrong path segment; update the logic that constructs the url (the const named
url using uuid and window.location.origin) to emit the route that matches the
app routing contract (/teampsylog/:uuid) instead of `/teampsylog/head/${uuid}`
so shared links point to `/teampsylog/{uuid}` when uuid exists and to
`/teampsylog` when it does not.
In `@src/components/common/Header.tsx`:
- Around line 77-96: The commented block in Header.tsx currently wraps both Link
components (href="/project" and href="/teampsylog"), unintentionally disabling
the 팀피셜록 link; update the JSX so only the 프로젝트 Link (Link with href="/project")
is commented/removed while leaving the 팀피셜록 Link (Link with href="/teampsylog")
active — i.e., split the comment boundaries to exclude the href="/teampsylog"
Link or uncomment that specific Link so the 팀피셜록 navigation appears in the
desktop header.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7353a189-3d96-41de-b3dd-774333b3957c
⛔ Files ignored due to path filters (2)
public/images/share-kakao.pngis excluded by!**/*.pngpublic/images/share-link.pngis excluded by!**/*.png
📒 Files selected for processing (13)
src/app/(main)/page.tsxsrc/app/(main)/teampsylog/[uuid]/_components/KeywordListPage.tsxsrc/app/(main)/teampsylog/_components/CommentPage.tsxsrc/app/(main)/teampsylog/_components/KeywordBar.tsxsrc/app/(main)/teampsylog/_components/KeywordPage.tsxsrc/app/(main)/teampsylog/page.tsxsrc/app/layout.tsxsrc/components/common/Header.tsxsrc/components/common/KaKaoScript.tsxsrc/components/modal/LinkShareModal.tsxsrc/constants/ModalList.tssrc/types/global.d.tssrc/types/kakao.d.ts
| await navigator.clipboard.writeText(url); | ||
| addToast({ message: '링크가 복사되었어요' }); | ||
|
|
||
| openModal('linkShare'); | ||
| // addToast({ message: '링크가 복사되었어요' }); |
There was a problem hiding this comment.
클립보드 쓰기 실패 시 공유 플로우가 중단됩니다.
navigator.clipboard.writeText 실패(권한/브라우저 정책) 시 예외가 전파되어 openModal('linkShare')가 실행되지 않습니다. try/catch로 실패 토스트 또는 폴백 처리가 필요합니다.
수정 예시
- await navigator.clipboard.writeText(url);
-
- openModal('linkShare');
+ try {
+ await navigator.clipboard.writeText(url);
+ openModal('linkShare');
+ } catch {
+ addToast({
+ type: 'error',
+ title: '링크 복사에 실패했어요.',
+ message: '브라우저 권한을 확인해주세요.',
+ });
+ }🤖 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 `@src/app/`(main)/teampsylog/_components/KeywordBar.tsx around lines 65 - 68,
Wrap the navigator.clipboard.writeText(url) call in a try/catch inside the
KeywordBar component so a rejected promise doesn’t block the sharing flow: call
navigator.clipboard.writeText(url) inside try, and in catch call addToast (or
the existing toast helper) with a failure message and/or fallback behavior, but
always proceed to call openModal('linkShare') after the attempt; reference the
navigator.clipboard.writeText call, the openModal('linkShare') invocation, and
addToast (currently commented) when implementing the change.
| if (window.Kakao && !window.Kakao.isInitialized()) { | ||
| window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_JS_KEY!); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target file around the reported lines
FILE="src/components/common/KaKaoScript.tsx"
if [ ! -f "$FILE" ]; then
echo "MISSING_FILE:$FILE"
exit 0
fi
echo "== $FILE =="
nl -ba "$FILE" | sed -n '1,120p'Repository: TEAMFICIAL/teamficial-fe
Length of output: 174
🌐 Web query:
Kakao JavaScript SDK init method parameter requirements window.Kakao.init undefined behavior
💡 Result:
Kakao.init(...) parameter requirements (JS SDK)
- You must pass your app’s JavaScript key as a string (from [App] → [Platform key] → [JavaScript key]). The official guide shows
Kakao.init("JAVASCRIPT_KEY")and thenKakao.isInitialized()to verify success. (developers.kakao.com) - Your site domain must be registered under JavaScript SDK domain for that JavaScript key; otherwise requests from other domains are rejected. (developers.kakao.com)
What happens if you call Kakao.init(undefined) (or a non-string / placeholder)?
- The SDK throws an error like:
KakaoError: Kakao.init: App key must be provided. A Kakao DevTalk response notes this happens when the key is not a string (or you left a placeholder like'YOUR APP KEY'). (devtalk.kakao.com)
When window.Kakao (or window.Kakao.init) is undefined
This typically means the SDK object was never available in that runtime, e.g.:
- The Kakao JS SDK script didn’t load / couldn’t be referenced, leading to errors like
undefined is not an object (evaluating 'window.Kakao.isInitialized'); Kakao staff指出 this looks like “cannot reference the JS SDK” (script not loaded, earlier script error, or the object got overwritten). (devtalk.kakao.com) - SSR / non-browser execution (e.g., Next.js server render):
windowdoesn’t exist, sowindow.Kakaocan’t exist until client-side.
Practical guard (prevents “undefined” + catches missing key)
if (typeof window !== "undefined" && window.Kakao && !window.Kakao.isInitialized()) {
if (!process.env.NEXT_PUBLIC_KAKAO_JS_KEY) throw new Error("Missing Kakao JS key");
window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_JS_KEY);
}If you share your exact script tag + where you call init() (and whether you’re using SSR), I can pinpoint which of the above is causing your undefined case.
Citations:
- 1: https://developers.kakao.com/docs/latest/en/javascript/getting-started
- 2: https://developers.kakao.com/docs/latest/en/app-setting/app
- 3: https://devtalk.kakao.com/t/javascript-sdk-cors-init-feat-kakao-login/114073
- 4: https://devtalk.kakao.com/t/mac-os-big-sur-safari-kakao-isinitialized/132097
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/components/common/KaKaoScript.tsx"
echo "== cat $FILE (first 80 lines) =="
sed -n '1,80p' "$FILE" | cat -n
echo
echo "== grep Kakao.init usages =="
rg -n "Kakao\.init|NEXT_PUBLIC_KAKAO_JS_KEY" "$FILE" || trueRepository: TEAMFICIAL/teamficial-fe
Length of output: 880
환경 변수 미설정 시 Kakao 초기화가 깨지지 않도록 init 호출 가드 추가 필요
src/components/common/KaKaoScript.tsx에서onLoad중window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_JS_KEY!)로 키를 단정하고 있어, 키가 비어/미설정이면 SDK가 초기화에 실패할 수 있습니다(“App key must be provided” 류 오류).
수정 예시
onLoad={() => {
- if (window.Kakao && !window.Kakao.isInitialized()) {
- window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_JS_KEY!);
+ const kakaoKey = process.env.NEXT_PUBLIC_KAKAO_JS_KEY;
+ if (window.Kakao && !window.Kakao.isInitialized() && kakaoKey) {
+ window.Kakao.init(kakaoKey);
}
}}📝 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.
| if (window.Kakao && !window.Kakao.isInitialized()) { | |
| window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_JS_KEY!); | |
| } | |
| onLoad={() => { | |
| const kakaoKey = process.env.NEXT_PUBLIC_KAKAO_JS_KEY; | |
| if (window.Kakao && !window.Kakao.isInitialized() && kakaoKey) { | |
| window.Kakao.init(kakaoKey); | |
| } | |
| }} |
🤖 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 `@src/components/common/KaKaoScript.tsx` around lines 13 - 15, The Kakao SDK
init call in KaKaoScript.tsx currently uses a non-null assertion on
process.env.NEXT_PUBLIC_KAKAO_JS_KEY and will throw if the env var is missing;
change the onLoad logic to first check that process.env.NEXT_PUBLIC_KAKAO_JS_KEY
(or a runtime variable passed into the component) is a non-empty string before
calling window.Kakao.init, skip/init only when window.Kakao exists and
!window.Kakao.isInitialized(), and optionally emit a console.warn or
processLogger message when the key is not set to make the failure visible
without breaking the app; reference window.Kakao and the init call in
KaKaoScript.tsx to locate the change.
| const getShareUrl = () => { | ||
| const uuid = window.location.pathname.split('/').pop(); | ||
| return `${window.location.origin}/teampsylog/head/${uuid}`; | ||
| }; |
There was a problem hiding this comment.
공유 URL 경로가 라우팅 계약과 불일치할 가능성이 큽니다.
Line 13에서 /teampsylog/head/${uuid}를 만들고 있는데, 현재 PR 목표는 UUID 기반 /teampsylog/:uuid 진입입니다. 이대로면 공유 링크가 잘못된 경로를 가리킬 수 있습니다.
수정 예시
const getShareUrl = () => {
const uuid = window.location.pathname.split('/').pop();
- return `${window.location.origin}/teampsylog/head/${uuid}`;
+ return `${window.location.origin}/teampsylog/${uuid}`;
};📝 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.
| const getShareUrl = () => { | |
| const uuid = window.location.pathname.split('/').pop(); | |
| return `${window.location.origin}/teampsylog/head/${uuid}`; | |
| }; | |
| const getShareUrl = () => { | |
| const uuid = window.location.pathname.split('/').pop(); | |
| return `${window.location.origin}/teampsylog/${uuid}`; | |
| }; |
🤖 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 `@src/components/modal/LinkShareModal.tsx` around lines 11 - 14, The
getShareUrl function builds a share link that currently points to
/teampsylog/head/${uuid} which mismatches the agreed routing of
/teampsylog/:uuid; update getShareUrl to construct the URL using the UUID only
(i.e., change the path to /teampsylog/${uuid}), keep UUID extraction via
window.location.pathname.split('/').pop(), and ensure the returned value uses
window.location.origin combined with the corrected path so shared links hit the
proper route.
| objectType: 'feed', | ||
| content: { | ||
| title: '팀피셜 (Teamficial)', | ||
| description: `${userName}님이 협업 후기를 기다리고 있어요!`, |
There was a problem hiding this comment.
userName null 케이스를 처리해주세요.
Line 28은 userName이 null일 때 null님... 문구가 노출됩니다. 기본 문구 fallback을 두는 게 안전합니다.
수정 예시
- description: `${userName}님이 협업 후기를 기다리고 있어요!`,
+ description: `${userName ?? '팀원'}님이 협업 후기를 기다리고 있어요!`,🤖 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 `@src/components/modal/LinkShareModal.tsx` at line 28, The description string
in LinkShareModal uses userName as-is, which yields "null님..." when userName is
null; update the description generation (where description: `${userName}님이 협업
후기를 기다리고 있어요!` is set) to guard against null/undefined by providing a fallback
display name (e.g., use a default like '사용자' or '누군가' or a localized fallback)
or conditionally render an alternative sentence when userName is missing so the
UI never shows "null님".
✅ PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
📸 스크린샷 or 실행영상
🎸 기타 사항 or 추가 코멘트
Summary by CodeRabbit
릴리스 노트
New Features
Improvements