Conversation
- url, introDescription만 공유
- React Native WebView 환경 감지 시 `postMessage`를 통한 네이티브 공유 통신 로직 추가 - RN 환경에서 공유 시 URL이 텍스트에 포함되도록 페이로드 구성 (웹뷰 호환성 개선) - 믹스패널 이벤트 트래킹에 `native_share` 메서드 케이스 추가
- 공유 미리보기를 위한 동아리 로고(`image`) 데이터 페이로드에 추가
This reverts commit e9b3a70.
This reverts commit 06d2163.
- Web Share API에서 url 필드 제거하고 text에 URL 포함 - 모바일 웹/데스크탑/앱 모든 환경에서 "메시지 + URL" 순서로 통일 - 카카오톡 등 일부 플랫폼에서 URL만 공유되던 문제 해결
- Web Share API 실패 시 클립보드 복사로 폴백 처리
…-542 [feature] 카카오톡 공유 및 web share api로 변경
- hideOn: 해더를 가릴 디바이스 타입을 설정합니다 - showOn: 해더를 보여줄 디바이스 타입을 설정합니다
- Vite 환경에서 SVG를 React 컴포넌트로 바로 import(`*.svg?react`)할 수 있도록 `vite-plugin-svgr` 플러그인 설치 및 설정 - TypeScript가 확장자를 인식할 수 있도록 타입 정의(`vite-env.d.ts`) 추가
- 앱 환경(`isInApp`)에서만 알림 버튼이 표시되도록 처리
- 앱 환경(React Native WebView)과 통신하기 위한 postMessage 래퍼 함수 구현 - 알림 구독/취소 및 뒤로가기 요청 기능
- 기존 헤더를 새로 구현된 MobileHeader로 교체 - 스크롤 인터랙션 및 탭 네비게이션 연동
- 스크롤 로직 공통화를 위한 useScrollTo 커스텀 훅 생성 - ScrollToTopButton 및 ClubDetailPage에 새로운 훅 적용 - 상세페이지 탭 전환 시 히스토리가 쌓이지 않도록 replace 옵션 적용하여 뒤로가기 동작 개선
[fix] 공유하기 웹뷰 브리징 제거
[fix] 이미지가 정상적으로 불러와지지않는 문제 수정
이유: 기존 env(safe-area-inset-top)가 WebView 환경에서 올바르게 동작하지 않는 문제를 해결하기 위해, React Native에서 직접 주입해주는 CSS 변수(--rn-safe-top)를 사용하도록 변경하여 기기별 노치 영역 대응을 보장함.
이유: ShareButton에 파편화되어 있던 React Native 통신 로직을 공통 유틸리티인 webviewBridge.ts로 통합하여, 통신 규격을 한곳에서 관리하고 컴포넌트의 책임을 단순화하기 위함.
…dge-MOA-570 [feature] 공유하기 웹뷰 브리지 복구
…-568 [fix] Webview 기기별 Safe Area 미적용 문제 해결 및 CSS 변수 기반 대응
…oading-MOA-536 [refactor] 활동사진을 최적화한다
다음은 라스트댄스 4인가요? ㅋㅋ |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx`:
- Around line 15-16: 현재 isRNWebView가 모듈 스코프에서 isInAppWebView()로 한 번만 평가되어 잘못된 값이
캐시될 위험이 있습니다; 이동할 위치는 ShareButton 컴포넌트 내부입니다 — 컴포넌트 렌더/마운트 시점에 isInAppWebView()를
평가하도록 isRNWebView를 ShareButton 내부로 옮기고(또는 성능을 위해 useMemo 또는 useState+useEffect로
래핑) 컴포넌트 외부의 모듈 스코프 참조를 제거하세요; 대상 식별자: isRNWebView, isInAppWebView(),
ShareButton.
♻️ Duplicate comments (1)
frontend/src/utils/webviewBridge.ts (1)
37-42: 옵셔널 체이닝으로 인한 잠재적 사일런트 실패이전 리뷰에서 지적된 것처럼,
window.ReactNativeWebView?.postMessage에서ReactNativeWebView가 정의되지 않은 경우에도return true가 실행됩니다. 명시적 존재 여부 확인이 필요합니다.
🧹 Nitpick comments (3)
frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx (1)
51-58: 변수 섀도잉 발생:index가 상태 변수와 map 콜백 파라미터에서 동일하게 사용됨Line 22에서 선언된
index상태 변수가 Line 51의 map 콜백 파라미터index에 의해 가려집니다. 코드가 정상 동작하지만, 가독성과 유지보수성을 위해 구분되는 이름 사용을 권장합니다.♻️ 변수명 변경 제안
- {feed.map((f, index) => ( - <Styled.PhotoItem key={`${f}-${index}`} onClick={() => open(index)}> + {feed.map((f, idx) => ( + <Styled.PhotoItem key={`${f}-${idx}`} onClick={() => open(idx)}> <Styled.PhotoImage src={f} - alt={`활동사진 ${index + 1}`} - loading={index < loadingThreshold ? 'eager' : 'lazy'} + alt={`활동사진 ${idx + 1}`} + loading={idx < loadingThreshold ? 'eager' : 'lazy'} /> </Styled.PhotoItem> ))}frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (2)
122-127: 인라인 스타일 대신 styled-components 활용 고려
display토글을 위해 인라인 스타일을 사용하고 있습니다. 프로젝트가 styled-components를 사용하므로, 일관성을 위해 스타일드 컴포넌트로 처리하는 것을 고려해 보세요. 현재 방식은 컴포넌트를 마운트 상태로 유지하여 상태 보존에는 유리하지만, 리소스 사용 측면에서는 고려가 필요합니다.♻️ 스타일드 컴포넌트 활용 예시
+ // ClubDetailPage.styles.ts에 추가 + export const TabPanel = styled.div<{ $visible: boolean }>` + display: ${({ $visible }) => ($visible ? 'block' : 'none')}; + `;<Styled.TabContent> - <div style={{ display: activeTab === TAB_TYPE.INTRO ? 'block' : 'none' }}> + <Styled.TabPanel $visible={activeTab === TAB_TYPE.INTRO}> <ClubIntroContent {...clubDetail.description} /> - </div> - <div style={{ display: activeTab === TAB_TYPE.PHOTOS ? 'block' : 'none' }}> + </Styled.TabPanel> + <Styled.TabPanel $visible={activeTab === TAB_TYPE.PHOTOS}> <ClubFeed feed={clubDetail.feeds} clubName={clubDetail.name} /> - </div> + </Styled.TabPanel> </Styled.TabContent>
25-26:TOP_BAR_HEIGHT매직 넘버 관리 고려
TOP_BAR_HEIGHT = 50이 하드코딩되어 있습니다.ClubDetailTopBar의 실제 CSS 높이와 동기화가 필요하며, 값이 변경될 경우 두 곳을 모두 수정해야 합니다. 공유 상수 파일로 추출하거나 CSS 변수를 활용하는 것을 고려해 보세요.
| const isRNWebView = isInAppWebView(); | ||
|
|
There was a problem hiding this comment.
isRNWebView가 모듈 스코프에서 평가되어 잠재적 문제 발생 가능
isInAppWebView()가 모듈 로드 시점에 한 번만 평가됩니다. 환경이 완전히 준비되기 전에 모듈이 로드되면 잘못된 값이 캐시될 수 있습니다. 컴포넌트 내부에서 평가하거나 useMemo를 사용하는 것이 더 안전합니다.
🔧 컴포넌트 내부로 이동 제안
- const isRNWebView = isInAppWebView();
-
const MOADONG_BASE_URL = 'https://www.moadong.com/club/';
const ShareButton = ({ clubId }: ShareButtonProps) => {
const { isMobile } = useDevice();
const { data: clubDetail } = useGetClubDetail(clubId);
const trackEvent = useMixpanelTrack();
+ const isRNWebView = isInAppWebView();
if (!clubDetail) return;🤖 Prompt for AI Agents
In `@frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx`
around lines 15 - 16, 현재 isRNWebView가 모듈 스코프에서 isInAppWebView()로 한 번만 평가되어 잘못된
값이 캐시될 위험이 있습니다; 이동할 위치는 ShareButton 컴포넌트 내부입니다 — 컴포넌트 렌더/마운트 시점에
isInAppWebView()를 평가하도록 isRNWebView를 ShareButton 내부로 옮기고(또는 성능을 위해 useMemo 또는
useState+useEffect로 래핑) 컴포넌트 외부의 모듈 스코프 참조를 제거하세요; 대상 식별자: isRNWebView,
isInAppWebView(), ShareButton.
📋 개요
이번 릴리즈는 동아리 상세 페이지의 전반적인 UX 개선과 안정적인 앱-웹 통신(WebView Bridge) 기반 마련을 목표로 합니다.
특히 기기별 Safe Area 대응 문제와 파편화되어 있던 공유 로직을 통합하여, 앱 내 웹뷰 환경에서도 네이티브 앱과 같은 매끄러운 사용자 경험을 제공하고자 합니다.
✨ 주요 변경 사항
1. 동아리 상세 페이지 UX/UI 고도화
기기별 Safe Area 대응 체계 구축
env(safe-area-inset-top)이 WebView 환경에서 일관되게 동작하지 않던 문제(0으로 반환 등)를 해결--rn-safe-top방식 도입모바일/태블릿 전용 탑바(ClubDetailTopBar) 도입
상세 페이지 레이아웃 개선
useScrollTo훅 도입2. WebView Bridge 시스템 인프라 구축
통신 로직 중앙화 (
webviewBridge.ts)postMessage통신 규격을 타입 안전하게 정의requestShare,requestNavigateBack등 액션 헬퍼 함수로 통신 로직 추상화앱-웹 UI 동기화
3. 공유하기 기능 개선 및 통합
플랫폼별 최적화 공유 로직
데이터 포맷 통일
4. 기술 부채 해결 및 성능 최적화
빌드 시스템 개선
타입 안정성 확보
이미지 로딩 최적화
📦 변경 사항 요약