Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughFeed와 마이페이지에서 LoadingSpinner 기반의 초기/탭 전환 로딩 흐름을 도입하고 FollowList의 초기 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as 사용자
participant F as Feed 페이지
participant S as API/서비스
participant L as LoadingSpinner
participant T as TotalFeed
participant M as MyFeed
rect rgba(200,220,255,0.18)
note over F: 초기 진입 로딩 (initialLoading)
U->>F: 페이지 진입
F->>L: initialLoading=true (스피너 표시)
F->>S: 토큰 대기 후 초기 피드 로드(loadTotalFeeds 또는 loadMyFeeds)
S-->>F: 데이터 반환 / 실패
F->>L: initialLoading=false (스피너 숨김)
alt 활성 탭 = 전체
F->>T: 데이터 전달 및 렌더
else 활성 탭 = 내 피드
F->>M: 데이터 전달 및 렌더
end
end
sequenceDiagram
autonumber
actor U as 사용자
participant F as Feed 페이지
participant S as API/서비스
participant L as LoadingSpinner
rect rgba(220,255,220,0.18)
note over F: 탭 전환 로딩 (tabLoading)
U->>F: 탭 전환(전체 ↔ 내 피드)
F->>L: tabLoading=true (스피너 표시)
F->>S: 해당 탭 피드 로드(loadTotalFeeds / loadMyFeeds)
S-->>F: 데이터 반환 / 실패
F->>L: tabLoading=false (스피너 숨김)
F->>F: 화면 갱신(선택 탭 내용 렌더)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/components/feed/MyFeed.tsx (1)
36-38: 로딩 시 빈 프래그먼트 반환 → 실패 시 영구 공백 가능. 최소 폴백 UI/에러 처리 추가 권장스피너를 Feed.tsx로 일원화한 의도는 👍. 다만 여기서 (loading || !profileData)일 때 빈 프래그먼트를 반환하면, getMyProfile 실패 시 profileData가 끝내 null이라 화면이 영구히 비게 됩니다. 최소한 에러 폴백 또는 메시지를 보여주도록 분기하는 편이 UX/디버깅 양면에서 안전합니다.
아래처럼 로딩/에러를 분리해 폴백을 추가하는 것을 권장합니다.
- if (loading || !profileData) { - return ( - <></> - // <LoadingSpinner message="내 피드 정보를 불러오는 중..." size="large" fullHeight={true} /> - ); - } + if (loading) return null; // 상위 Feed.tsx 스피너에 위임 + + if (!profileData) { + return ( + <EmptyState> + <div>내 피드 정보를 불러오지 못했어요. 잠시 후 다시 시도해주세요.</div> + </EmptyState> + ); + }추가로, 포스트 리스트 렌더 시 key에 index를 포함하면 재정렬/추가 시 비효율적인 리렌더가 발생할 수 있습니다. feedId가 유니크하다면 아래처럼 단일 키를 권장합니다.
- key={`${post.feedId}-${index}`} + key={post.feedId}해당 변경 후, 토큰이 없거나 프로필 API가 실패하는 상황에서 더 이상 빈 화면이 유지되지 않는지 한 번 확인 부탁드립니다.
src/components/common/Post/PostBody.tsx (1)
96-101: 이미지 컨테이너 제거로 갭 복구는 적절. 다만 img 접근성/성능 개선과 key 안정성 보완 권장현재 변경으로 flex gap이 이미지 사이에 정상 적용됩니다. 다음의 소소한 개선을 제안합니다.
- key: index 대신 src(또는 고유 ID)를 사용해 리렌더 최소화
- 접근성: alt 텍스트 추가
- 성능: lazy 로딩/비동기 디코딩
아래처럼 보완하면 좋습니다.
- <> - {contentUrls.map((src: string, i: number) => ( - <img key={i} src={src} /> - ))} - </> + <> + {contentUrls.map((src: string) => ( + <img key={src} src={src} alt="피드 이미지" loading="lazy" decoding="async" /> + ))} + </>또한 말줄임 여부 감지 useEffect는 hasImage에 따라 line-clamp가 달라지므로 의존성에 hasImage를 포함하는 편이 안전합니다.
useEffect(() => { if (contentRef.current) { const element = contentRef.current; const isOverflowing = element.scrollHeight > element.clientHeight; setIsTruncated(isOverflowing); } }, [contentBody, hasImage]);그리고 이전에 .imgContainer를 대상으로 한 CSS가 있었다면 깨질 수 있으므로, 전역/다른 스타일에서 해당 클래스 사용 여부를 한 번 점검해 주세요.
프로젝트 내에서
.imgContainer또는imgContainer클래스 사용 여부를 검색해 잔존 의존성이 없는지 확인해 주세요.src/components/feed/FollowList.tsx (1)
13-13: 초기 로딩 true로 변경은 상위 스피너 전략과 일관. 단, 스피너 종료 이후의 공백 대비 필요Feed.tsx에서 초기/탭 로딩을 스피너로 일원화했다면, 여기서 초기 로딩을 true로 둬 첫 렌더를 지연시키는 방향은 일관성이 있습니다. 다만 상위 스피너가 내려간 뒤에도 이 컴포넌트의 로딩이 끝나지 않았다면, 섹션이 빈 상태로 남을 수 있습니다. 사용자 체감 개선을 위해 간단한 스켈레톤(가로 스크롤용 원형 아바타 더미 등) 표시를 고려해 보세요. 상위 스피너와 중복 로딩 지표가 헷갈리지 않도록, 스켈레톤은 최소한의 공간만 점유하는 형태가 좋습니다.
상위 스피너 해제 직후 FollowList가 빈 상태로 남는 순간(플리커)이 있는지 실제 단말에서 한 번 확인 부탁드립니다.
src/pages/feed/Feed.tsx (1)
191-204: 불필요한 Fragment 중첩 제거로 DOM/가독성 정리 가능JSX에서 중첩된 빈 Fragment는 의미가 없습니다. 아래처럼 정리하면 DOM도 줄고 가독성도 개선됩니다.
- <> - {activeTab === '피드' ? ( - <> - <TotalFeed - showHeader={true} - posts={totalFeedPosts} - isMyFeed={false} - isLast={totalIsLast} - /> - </> - ) : ( - <> - <MyFeed showHeader={false} posts={myFeedPosts} isMyFeed={true} isLast={myIsLast} /> - </> - )} - </> + {activeTab === '피드' ? ( + <TotalFeed + showHeader={true} + posts={totalFeedPosts} + isMyFeed={false} + isLast={totalIsLast} + /> + ) : ( + <MyFeed showHeader={false} posts={myFeedPosts} isMyFeed={true} isLast={myIsLast} /> + )}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/components/common/Post/PostBody.tsx(1 hunks)src/components/feed/FollowList.tsx(1 hunks)src/components/feed/MyFeed.tsx(1 hunks)src/pages/feed/Feed.tsx(4 hunks)
🔇 Additional comments (2)
src/pages/feed/Feed.tsx (2)
8-8: 로딩 스피너 도입과 위치 선정 적절Feed 단에서 초기/탭 전환 로딩을 일원화한 방향이 명확하고, 사용자 인지에도 도움이 됩니다.
47-51: 초기/탭 로딩 상태 분리: 상태 모델링이 명확해졌습니다초기 진입과 탭 전환의 로딩 경험을 분리한 점 좋습니다. 이후 유지보수 시에도 의도 파악이 쉬워집니다.
| setTabLoading(true); | ||
|
|
||
| try { | ||
| if (activeTab === '피드') { | ||
| await loadTotalFeeds(); | ||
| } else if (activeTab === '내 피드') { | ||
| await loadMyFeeds(); | ||
| } | ||
| } finally { | ||
| setTabLoading(false); | ||
| setInitialLoading(false); | ||
| } |
There was a problem hiding this comment.
토큰 미존재 시 초기 로딩이 영구 지속되는 버그 가능성
현재 토큰이 없으면 Line 160-164에서 조기 return 되며, 이 분기는 try/finally 블록(여기, Line 169-177) 바깥이어서 setInitialLoading(false)가 호출되지 않습니다. 결과적으로 스피너가 영구히 떠 있을 수 있습니다.
최소 수정안: 토큰 체크를 try/finally 내부로 옮기거나, 조기 return 전에 initialLoading을 false로 내려주세요.
권장 수정 예시(토큰 체크를 try/finally 안으로 이동):
- // localStorage에 토큰이 있는지 확인
- const authToken = localStorage.getItem('authToken');
- if (!authToken) {
- console.log('❌ 토큰이 없어서 피드를 로드할 수 없습니다.');
- return;
- }
-
- setTabLoading(true);
- try {
- if (activeTab === '피드') {
- await loadTotalFeeds();
- } else if (activeTab === '내 피드') {
- await loadMyFeeds();
- }
- } finally {
- setTabLoading(false);
- setInitialLoading(false);
- }
+ setTabLoading(true);
+ try {
+ // localStorage에 토큰이 있는지 확인
+ const authToken = localStorage.getItem('authToken');
+ if (!authToken) {
+ console.log('❌ 토큰이 없어서 피드를 로드할 수 없습니다.');
+ return; // finally 보장
+ }
+ if (activeTab === '피드') {
+ await loadTotalFeeds();
+ } else if (activeTab === '내 피드') {
+ await loadMyFeeds();
+ }
+ } finally {
+ setTabLoading(false);
+ setInitialLoading(false);
+ }대안: 조기 return 직전에 setInitialLoading(false)를 호출해도 됩니다.
📝 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.
| setTabLoading(true); | |
| try { | |
| if (activeTab === '피드') { | |
| await loadTotalFeeds(); | |
| } else if (activeTab === '내 피드') { | |
| await loadMyFeeds(); | |
| } | |
| } finally { | |
| setTabLoading(false); | |
| setInitialLoading(false); | |
| } | |
| setTabLoading(true); | |
| try { | |
| // localStorage에 토큰이 있는지 확인 | |
| const authToken = localStorage.getItem('authToken'); | |
| if (!authToken) { | |
| console.log('❌ 토큰이 없어서 피드를 로드할 수 없습니다.'); | |
| return; // finally 블록에서 초기 로딩 상태가 해제됩니다 | |
| } | |
| if (activeTab === '피드') { | |
| await loadTotalFeeds(); | |
| } else if (activeTab === '내 피드') { | |
| await loadMyFeeds(); | |
| } | |
| } finally { | |
| setTabLoading(false); | |
| setInitialLoading(false); | |
| } |
🤖 Prompt for AI Agents
In src/pages/feed/Feed.tsx around lines 166-177, the early return when no token
occurs outside the try/finally block so setInitialLoading(false) (and
setTabLoading(false)) never runs, leaving the spinner active; move the token
existence check into the try block (so the finally always clears loading flags)
or, if you keep the early return, ensure you call setInitialLoading(false) (and
optionally setTabLoading(false)) immediately before returning.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pages/mypage/Mypage.tsx (1)
77-107: 보안: window.open에 noopener/noreferrer 추가로 탭낚시(tabnabbing) 방지
target='_blank'는 새 탭에서 원 탭의window.opener에 접근 가능합니다.noopener,noreferrer를 추가해 역탭낚시를 방지하세요.적용 예:
- window.open('https://slashpage.com/thip/7vgjr4m1nynpy2dwpy86', '_blank'); + window.open('https://slashpage.com/thip/7vgjr4m1nynpy2dwpy86', '_blank', 'noopener,noreferrer'); ... - window.open('https://slashpage.com/thip/ywk9j72989p6rmgpqvnd', '_blank'); + window.open('https://slashpage.com/thip/ywk9j72989p6rmgpqvnd', '_blank', 'noopener,noreferrer'); ... - window.open('https://slashpage.com/thip/dk58wg2e6yy3zmnqevxz', '_blank'); + window.open('https://slashpage.com/thip/dk58wg2e6yy3zmnqevxz', '_blank', 'noopener,noreferrer'); ... - window.open('https://slashpage.com/thip/7916x82r8y74n24kpyg3', '_blank'); + window.open('https://slashpage.com/thip/7916x82r8y74n24kpyg3', '_blank', 'noopener,noreferrer'); ... - window.open('https://slashpage.com/thip/1q3vdn2p9w93pmxy49pr', '_blank'); + window.open('https://slashpage.com/thip/1q3vdn2p9w93pmxy49pr', '_blank', 'noopener,noreferrer');비고:
noreferrer가 리퍼러 수집에 영향을 줄 수 있습니다. 최소한noopener는 반드시 포함하세요.- 대안:
const w = window.open(...); w?.opener = null;
🧹 Nitpick comments (6)
src/pages/mypage/Mypage.tsx (6)
21-21: 중복 로딩 상태 설정 제거 제안초기값을
true로 두고, 즉시setLoading(true)를 다시 호출하고 있어 중복입니다. 현재 구조에서는 효과가 동일합니다. 유지해도 문제는 없으나, 불필요 호출을 줄이는 쪽을 권장합니다.적용 예:
- setLoading(true);Also applies to: 29-29
26-39: 언마운트 후 setState 호출 방지 가드 추가 권장네트워크 응답이 느린 경우, 페이지 이탈(언마운트) 뒤에
setProfile/setLoading이 호출될 수 있습니다. 간단한 마운트 가드로 경고/메모리 누수를 예방하세요.적용 예:
useEffect(() => { + let isMounted = true; const fetchProfile = async () => { try { setLoading(true); - const profile = await getMyProfile(); - setProfile(profile); + const profileData = await getMyProfile(); + if (!isMounted) return; + setProfile(profileData); } catch (error) { - console.error('프로필 정보 로드 실패:', error); + if (!isMounted) return; + console.error('프로필 정보 로드 실패:', error); } finally { - setLoading(false); + if (isMounted) setLoading(false); } }; fetchProfile(); + return () => { + isMounted = false; + }; }, []);
45-51: 로딩 중에도 하단 네비게이션 유지 고려전체 스크린 로딩은 적절하지만, 유저의 즉시 이탈 경로(탭 이동 등)를 위해 NavBar를 로딩 화면에도 노출시키는 것을 권장합니다. UX 일관성 측면에서도 이점이 있습니다.
적용 예:
if (loading) { return ( <Wrapper> <LoadingSpinner message="내 정보를 불러오는 중..." size="large" fullHeight={true} /> + <NavBar /> </Wrapper> ); }
54-58: 에러 상태에서도 레이아웃 일관성 유지(헤더/네비 포함) 제안현재 에러 상태에서는 Header/Container/NavBar가 빠져 레이아웃과 이탈 경로가 달라집니다. 고정 헤더를 사용할 경우, 메시지가 헤더에 가려질 가능성도 있습니다. 아래처럼 구성하면 정상 상태와 UI/동선이 동일해집니다.
적용 예:
if (!profile) { return ( <Wrapper> - <div>프로필 정보를 불러올 수 없습니다.</div> + <Header>내 정보</Header> + <Container> + <div>프로필 정보를 불러올 수 없습니다.</div> + </Container> + <NavBar /> </Wrapper> ); }
114-120: 접근성/성능: 프로필 이미지에 alt/lazy 추가
img에alt가 없어 접근성 기준을 충족하지 못합니다. 지연 로딩으로 초기 렌더 비용도 줄일 수 있습니다.적용 예:
- <img src={profile.profileImageUrl} /> + <img + src={profile.profileImageUrl} + alt={`${profile.nickname} 프로필 이미지`} + loading="lazy" + referrerPolicy="no-referrer" + />
145-146: 문구 오탈자: ‘버젼’ → ‘버전’사용자 노출 텍스트로, 맞춤법 수정을 권장합니다.
적용 예:
- <MenuButton src={ver} name="버젼 1.0.0" isButton onClick={handleVersion} /> + <MenuButton src={ver} name="버전 1.0.0" isButton onClick={handleVersion} />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/pages/mypage/Mypage.tsx(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/pages/mypage/Mypage.tsx (3)
src/hooks/usePopupActions.ts (1)
usePopupActions(9-35)src/hooks/useLogout.ts (1)
useLogout(3-15)src/api/users/getMyProfile.ts (1)
getMyProfile(15-18)
🔇 Additional comments (1)
src/pages/mypage/Mypage.tsx (1)
5-5: LGTM: LoadingSpinner 도입 적절합니다로딩 UX 일관성을 위해 공통 스피너를 사용한 선택, 방향성 좋습니다.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
index.html (1)
10-10: 소셜 미리보기 안정성 향상을 위해 추가 메타 태그를 권장합니다.해상도 파싱 실패 시를 대비해
og:image:width,og:image:height를 명시하고, 트위터 카드 메타(twitter:card,twitter:image등)도 함께 선언하면 플랫폼별 일관성이 좋아집니다. 현재 https 사용 중이라og:image:secure_url은 선택 사항입니다.아래는 참고용 스니펫입니다(적절한 width/height 값으로 교체 필요):
<!-- Open Graph --> <meta property="og:image" content="https://thip.co.kr/assets/thumbnail.jpeg" /> <meta property="og:image:width" content="1200" /> <meta property="og:image:height" content="630" /> <!-- Twitter --> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:title" content="THIP, 독서를 기록하는 가장 힙한 방법" /> <meta name="twitter:description" content="커뮤니티형 독서 기록 플랫폼. 띱. THIP." /> <meta name="twitter:image" content="https://thip.co.kr/assets/thumbnail.jpeg" />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (3)
public/assets/custom_favicon.pngis excluded by!**/*.pngpublic/assets/thumbnail.jpegis excluded by!**/*.jpegpublic/assets/thumbnail.pngis excluded by!**/*.png
📒 Files selected for processing (1)
index.html(1 hunks)
| <meta property="og:title" content="THIP, 독서를 기록하는 가장 힙한 방법" /> | ||
| <meta property="og:description" content="커뮤니티형 독서 기록 플랫폼. 띱. THIP." /> | ||
| <meta property="og:image" content="https://thip.co.kr/assets/thumbnail.png" /> | ||
| <meta property="og:image" content="https://thip.co.kr/assets/thumbnail.jpeg" /> |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
OG 이미지 URL 전환(.png → .jpeg) 자체는 적절합니다만, 배포 전 헤더/용량/타입을 점검하세요.
소셜 미리보기 안정성을 위해 200 OK, Content-Type: image/jpeg, 적절한 Cache-Control, 과도하지 않은 용량(권장 < 1MB)을 확인하면 좋습니다. 또, 변경 직후 크롤러 캐시 영향이 있을 수 있으니 URL 접근성도 함께 확인 바랍니다.
다음 스크립트로 기본 헤더와 용량을 점검할 수 있습니다:
또한 최소 권장 해상도(예: 1200x630)에 부합하는지 확인이 필요하면, 아래 스크립트로 이미지 실제 해상도를 점검할 수 있습니다:
필요하시면 위 검증을 CI에 넣을 간단한 체크 스크립트도 마련해 드릴 수 있습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
URL="https://thip.co.kr/assets/thumbnail.jpeg"
echo "[HEAD 체크]"
curl -sI "$URL" | sed -n '1,20p'
echo
echo "[핵심 헤더 추출]"
curl -sI "$URL" | awk '
BEGIN{IGNORECASE=1}
/^HTTP\//{print "Status: "$0}
/^content-type:/{print}
/^content-length:/{print}
/^cache-control:/{print}
/^etag:/{print}
/^last-modified:/{print}
'
echo
echo "[용량(KB) 추정]"
LEN=$(curl -sI "$URL" | awk 'BEGIN{IGNORECASE=1}/^content-length:/{print $2}' | tr -d '\r')
if [[ -n "${LEN:-}" ]]; then
KB=$((LEN/1024))
echo "Content-Length: ${KB} KB"
else
echo "Content-Length 헤더 없음(전송 인코딩 chunked 가능성). 실제 파일 크기 확인 권장."
fiLength of output: 1151
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - << 'PY'
import io, sys
from urllib.request import urlopen
from PIL import Image # pillow 필요
url = "https://thip.co.kr/assets/thumbnail.jpeg"
with urlopen(url) as resp:
data = resp.read()
img = Image.open(io.BytesIO(data))
print(f"Format={img.format}, Size={img.size[0]}x{img.size[1]}")
w, h = img.size
if w < 1200 or h < 630:
print("WARN: 권장 최소 해상도(1200x630) 미만일 수 있습니다.")
else:
print("OK: 권장 해상도 충족.")
PYLength of output: 146
OG 이미지 HTTP 응답 검증 필요: 최종 URL 헤더·타입·용량 점검
소셜 미리뷰 안정화를 위해, 아래 사항을 꼭 확인하세요.
- 307 리다이렉트로 인해 초기 응답이 text/plain으로 돌아올 수 있음
→ curl -sI -L 로 최종 응답 헤더를 확인해야 함 - HTTP/2 200 OK
- Content-Type: image/jpeg
- Content-Length 헤더 존재(권장 < 1 MB)
- Cache-Control, ETag/Last-Modified 설정
- 크롤러 캐시 영향 방지
아래 스크립트로 기본 검증을 수행할 수 있습니다:
#!/usr/bin/env bash
set -euo pipefail
URL="https://thip.co.kr/assets/thumbnail.jpeg"
echo "[최종 응답 헤더 확인]"
curl -sI -L "$URL" | awk '
BEGIN{IGNORECASE=1}
/^HTTP\//{print "Status: "$0}
/^content-type:/{print}
/^content-length:/{print}
/^cache-control:/{print}
/^etag:/{print}
/^last-modified:/{print}
'
LEN=$(curl -sI -L "$URL" | awk 'BEGIN{IGNORECASE=1}/^content-length:/{print $2}' | tr -d '\r')
if [[ -n "$LEN" ]]; then
KB=$((LEN/1024))
echo "Content-Length: ${KB} KB"
else
echo "Content-Length 헤더 없음 (chunked encoding 가능성)"
fi필요 시 해상도(권장 1200×630 이상) 확인용 스크립트 예:
#!/usr/bin/env bash
set -euo pipefail
URL="https://thip.co.kr/assets/thumbnail.jpeg"
TMP="$(mktemp --suffix=.jpeg)"
curl -sL "$URL" -o "$TMP"
# ImageMagick identify 필요
identify -format "Format=%m, Size=%wx%h\n" "$TMP" || echo "ImageMagick 설치 필요"
rm -f "$TMP"CI에 통합하거나, Python+Pillow 환경이 준비되어 있으면 원래 스크립트를 그대로 사용하셔도 좋습니다.
🤖 Prompt for AI Agents
index.html lines 10-10: The OG image URL may be returning intermediate redirects
or incorrect headers (e.g., text/plain, missing Content-Length, wrong
Content-Type) which breaks social previews; verify and fix by ensuring the final
URL (follow redirects) responds with HTTP/2 200, Content-Type: image/jpeg, a
Content-Length header (preferably <1MB), and proper caching headers
(Cache-Control and ETag or Last-Modified); update the server/static
hosting/configuration so the thumbnail is served directly as an image (no
307->text/plain intermediates), add or enable Content-Length, and set caching
headers; additionally validate image dimensions (>=1200x630) and re-run a final
curl -sI -L and image inspection to confirm compliance.
#️⃣연관된 이슈
없음
📝작업 내용
Summary by CodeRabbit
신기능
리팩터
기타 변경