feat(room): 공간 카드에 실제 사진 소스와 교체 경로를 연결
맥락: - 오늘의 공간 카드를 단색에서 실제 사진 기반으로 전환하고, 사진 출처를 추적할 수 있는 링크가 필요했다. - 추후 자가 호스팅 이미지로 교체할 수 있도록 데이터 구조에서 전환 경로를 미리 확보해야 했다. 변경사항: - entities/room 모델에 cardPhotoUrl, googleImageSearchUrl, managedCardPhotoUrl 필드를 추가했다. - 10개 공간 카드에 실제 사진 URL과 Google 이미지 검색 링크를 매핑했다. - getRoomCardPhotoUrl, getRoomCardBackgroundStyle helper를 추가해 추후 managed 이미지로 교체 가능한 fallback 구조를 만들었다. - RoomPreviewCard 배경을 실제 사진 렌더링으로 변경하고, 텍스트 가독성을 위한 오버레이를 적용했다. - 세션 문서(90_current_state, session_brief)에 이번 작업 내역과 리스크를 반영했다. 검증: - npx tsc --noEmit 세션-상태: 오늘의 공간 카드에 실제 사진 소스/구글 링크/교체 경로 반영 완료 세션-다음: RoomSheet/도크 패널의 인원수 기반 표현을 분위기형 정보로 전환 세션-리스크: 외부 이미지 URL 의존으로 소스 만료/속도 이슈 가능성이 있어 자가 호스팅 전환 필요
This commit is contained in:
@@ -64,6 +64,10 @@ Last Updated: 2026-02-28
|
||||
- `entities/room`에 룸별 `hubColor`를 추가하고 색이 서로 겹치지 않도록 분리
|
||||
- 오늘의 공간 카드는 이미지 대신 룸 고유 단색 배경으로 렌더링
|
||||
- 허브 페이지 배경도 선택된 룸의 동일 색으로 전환되도록 연결
|
||||
- `/app` 오늘의 공간 카드에 실제 사진(외부 URL) 적용:
|
||||
- `entities/room`에 `cardPhotoUrl`, `googleImageSearchUrl`, `managedCardPhotoUrl` 필드 추가
|
||||
- 카드 배경은 실제 사진을 사용하고, Google 이미지 검색 링크를 룸별로 보관
|
||||
- 추후 `managedCardPhotoUrl`만 채우면 자가 관리 이미지로 전환되도록 fallback helper 추가
|
||||
- 몰입 모드 ON 시 `/space` 크롬 정리:
|
||||
- 상단 `Current Room` 블록 숨김
|
||||
- 우상단 허브 버튼 소형 아이콘화
|
||||
@@ -100,6 +104,7 @@ Last Updated: 2026-02-28
|
||||
- 모달 본문 고정 높이 적용으로 작은 화면에서 내부 스크롤 의존도가 이전보다 높아질 수 있음
|
||||
- 룸 프리뷰 카드 내부는 이미지 기반 다크 텍스트 체계라 컨테이너와 톤 차이가 남아 추가 톤 정리가 필요할 수 있음
|
||||
- 단색 팔레트가 일부 기기 색감에서 유사하게 보일 수 있어 실제 디스플레이 기준 재점검 필요
|
||||
- 외부 이미지 URL(임시) 의존이라 소스 만료/속도 이슈 가능성이 있어 자가 호스팅 전환 필요
|
||||
|
||||
## CHANGED FILES
|
||||
|
||||
@@ -177,3 +182,4 @@ Last Updated: 2026-02-28
|
||||
7. 커스텀 입장 모달 탭 전환(공간/사운드/타이머) 시 외곽 모달 크기가 유지됨
|
||||
8. `/app`: 시작 카드/공간 카드가 흰 표면 + 어두운 텍스트로 표시되고, 카드 밖 배경은 블러된 가상 공간으로 노출됨
|
||||
9. `/app`: 오늘의 공간 카드가 룸별 단색으로 표시되고, 허브 배경도 선택 카드 색으로 동기화됨
|
||||
10. `/app`: 오늘의 공간 카드가 실제 사진으로 표시되고, room 데이터에 Google 검색 링크/자가 이미지 전환 필드가 포함됨
|
||||
|
||||
@@ -54,6 +54,9 @@ Last Updated: 2026-02-28
|
||||
- `/app` 오늘의 공간 카드를 룸별 단색 팔레트로 전환했다.
|
||||
- `entities/room`에 `hubColor`를 추가해 카드별 고유 단색을 분리했다.
|
||||
- 허브 배경도 선택 룸의 같은 색으로 동기화해 톤 연결성을 높였다.
|
||||
- `/app` 오늘의 공간 카드에 실제 사진(임시 외부 URL)을 연결했다.
|
||||
- `entities/room`에 `cardPhotoUrl`, `googleImageSearchUrl`, `managedCardPhotoUrl`를 추가했다.
|
||||
- Google 이미지 검색 링크를 룸별로 보관하고, 추후 자가 이미지 경로로 전환 가능한 fallback 구조를 적용했다.
|
||||
- 몰입 모드 ON 시 상단 룸 블록 숨김, 레일 미니화, HUD 저대비, 비네팅 강화가 적용된다.
|
||||
- 이후 작업은 `docs/work.md`를 기준으로 실행한다.
|
||||
|
||||
@@ -70,6 +73,7 @@ Last Updated: 2026-02-28
|
||||
- 모달 고정 높이로 인해 작은 화면에서는 탭 본문 내부 스크롤 사용 빈도가 늘 수 있음
|
||||
- 룸 프리뷰 카드의 이미지 기반 다크 톤과 라이트 컨테이너 사이 미세한 톤 차이가 남을 수 있음
|
||||
- 단색 팔레트가 일부 디스플레이에서 유사하게 보일 수 있어 기기별 색 분리도 점검이 필요함
|
||||
- 외부 이미지 URL 의존으로 소스 만료/속도 이슈 가능성이 있어 자가 호스팅 전환이 필요함
|
||||
|
||||
## 상세 원문 위치
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '밤',
|
||||
vibeLabel: '잔잔함',
|
||||
hubColor: '#D6E6F7',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/16763533/pexels-photo-16763533.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=rainy+window+interior+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 32,
|
||||
presence: { focus: 23, break: 6, away: 3 },
|
||||
previewImage: '/spaces/rain-window.jpg',
|
||||
@@ -26,6 +31,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '새벽',
|
||||
vibeLabel: '포근함',
|
||||
hubColor: '#F5DDCB',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/18340237/pexels-photo-18340237.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=dawn+cafe+interior+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 28,
|
||||
presence: { focus: 18, break: 7, away: 3 },
|
||||
previewImage: '/spaces/dawn-cafe.jpg',
|
||||
@@ -41,6 +51,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '오후',
|
||||
vibeLabel: '몰입',
|
||||
hubColor: '#DCE4D1',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/31390421/pexels-photo-31390421.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=quiet+library+interior+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 41,
|
||||
presence: { focus: 31, break: 7, away: 3 },
|
||||
previewImage: '/spaces/library.jpg',
|
||||
@@ -56,6 +71,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '밤',
|
||||
vibeLabel: '차분함',
|
||||
hubColor: '#CFE9EA',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/12715501/pexels-photo-12715501.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=sea+view+room+interior+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 19,
|
||||
presence: { focus: 13, break: 4, away: 2 },
|
||||
previewImage: '/spaces/ocean.jpg',
|
||||
@@ -71,6 +91,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '오전',
|
||||
vibeLabel: '맑음',
|
||||
hubColor: '#D1E7C9',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/34503448/pexels-photo-34503448.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=forest+cabin+interior+window+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 26,
|
||||
presence: { focus: 17, break: 6, away: 3 },
|
||||
previewImage: '/spaces/forest.jpg',
|
||||
@@ -86,6 +111,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '밤',
|
||||
vibeLabel: '온기',
|
||||
hubColor: '#F2D4C0',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/14353716/pexels-photo-14353716.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=cozy+fireplace+living+room+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 21,
|
||||
presence: { focus: 15, break: 4, away: 2 },
|
||||
previewImage: '/spaces/fireplace.jpg',
|
||||
@@ -101,6 +131,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '심야',
|
||||
vibeLabel: '고요함',
|
||||
hubColor: '#D9D3ED',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/17663181/pexels-photo-17663181.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=city+night+apartment+interior+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 34,
|
||||
presence: { focus: 24, break: 7, away: 3 },
|
||||
previewImage: '/spaces/city-night.jpg',
|
||||
@@ -116,6 +151,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '새벽',
|
||||
vibeLabel: '선명함',
|
||||
hubColor: '#D8E7F3',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/34340672/pexels-photo-34340672.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=snow+mountain+window+view+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 15,
|
||||
presence: { focus: 11, break: 3, away: 1 },
|
||||
previewImage: '/spaces/snow-mountain.jpg',
|
||||
@@ -131,6 +171,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '오후',
|
||||
vibeLabel: '가벼움',
|
||||
hubColor: '#F6EDC7',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/34833126/pexels-photo-34833126.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=sunlit+room+window+interior+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 27,
|
||||
presence: { focus: 18, break: 6, away: 3 },
|
||||
previewImage: '/spaces/sun-window.jpg',
|
||||
@@ -146,6 +191,11 @@ export const ROOM_THEMES: RoomTheme[] = [
|
||||
recommendedTime: '심야',
|
||||
vibeLabel: '깊음',
|
||||
hubColor: '#D4DCF4',
|
||||
cardPhotoUrl:
|
||||
'https://images.pexels.com/photos/18537868/pexels-photo-18537868.jpeg?auto=compress&cs=tinysrgb&w=1400',
|
||||
googleImageSearchUrl:
|
||||
'https://www.google.com/search?tbm=isch&q=milky+way+night+sky+photo',
|
||||
managedCardPhotoUrl: null,
|
||||
activeMembers: 23,
|
||||
presence: { focus: 17, break: 4, away: 2 },
|
||||
previewImage: '/spaces/outer-space.jpg',
|
||||
@@ -158,6 +208,19 @@ export const getRoomById = (roomId: string) => {
|
||||
return ROOM_THEMES.find((room) => room.id === roomId);
|
||||
};
|
||||
|
||||
export const getRoomCardPhotoUrl = (room: RoomTheme) => {
|
||||
// Swap managedCardPhotoUrl when you start serving first-party assets.
|
||||
return room.managedCardPhotoUrl ?? room.cardPhotoUrl;
|
||||
};
|
||||
|
||||
export const getRoomCardBackgroundStyle = (room: RoomTheme): CSSProperties => {
|
||||
return {
|
||||
backgroundImage: `url('${getRoomCardPhotoUrl(room)}')`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
};
|
||||
};
|
||||
|
||||
export const getRoomBackgroundStyle = (room: RoomTheme): CSSProperties => {
|
||||
return {
|
||||
backgroundImage: `${room.previewGradient}, url('${room.previewImage}')`,
|
||||
|
||||
@@ -15,6 +15,9 @@ export interface RoomTheme {
|
||||
recommendedTime: string;
|
||||
vibeLabel: string;
|
||||
hubColor: string;
|
||||
cardPhotoUrl: string;
|
||||
googleImageSearchUrl: string;
|
||||
managedCardPhotoUrl: string | null;
|
||||
activeMembers: number;
|
||||
presence: RoomPresence;
|
||||
previewImage: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RoomTheme } from '@/entities/room';
|
||||
import { getRoomCardBackgroundStyle, type RoomTheme } from '@/entities/room';
|
||||
import { cn } from '@/shared/lib/cn';
|
||||
|
||||
interface RoomPreviewCardProps {
|
||||
@@ -26,22 +26,22 @@ export const RoomPreviewCard = ({
|
||||
<div
|
||||
aria-hidden
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundColor: room.hubColor,
|
||||
}}
|
||||
style={getRoomCardBackgroundStyle(room)}
|
||||
/>
|
||||
<div aria-hidden className="absolute inset-0 bg-slate-900/28" />
|
||||
<div aria-hidden className="absolute inset-0 bg-[linear-gradient(to_top,rgba(2,6,23,0.64),rgba(2,6,23,0.2))]" />
|
||||
|
||||
<div className="relative space-y-3 rounded-xl border border-brand-dark/10 bg-white/42 p-3 backdrop-blur-[1px]">
|
||||
<div className="relative space-y-3 rounded-xl border border-white/20 bg-slate-950/22 p-3 backdrop-blur-[1.5px]">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-brand-dark">{room.name}</h3>
|
||||
<p className="mt-1 text-xs text-brand-dark/72">{room.description}</p>
|
||||
<h3 className="text-base font-semibold text-white">{room.name}</h3>
|
||||
<p className="mt-1 text-xs text-white/82">{room.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{room.tags.map((tag) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="inline-flex items-center gap-1 rounded-full bg-white/62 px-3 py-1.5 text-xs font-medium text-brand-dark/86 ring-1 ring-brand-dark/12"
|
||||
className="inline-flex items-center gap-1 rounded-full bg-white/16 px-3 py-1.5 text-xs font-medium text-white/92 ring-1 ring-white/22"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
@@ -49,14 +49,14 @@ export const RoomPreviewCard = ({
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-brand-dark/78">
|
||||
추천 사운드: <span className="font-medium text-brand-dark">{room.recommendedSound}</span>
|
||||
<p className="text-xs text-white/84">
|
||||
추천 사운드: <span className="font-medium text-white">{room.recommendedSound}</span>
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-white/58 px-3 py-1.5 text-xs font-medium text-brand-dark/84 ring-1 ring-brand-dark/12">
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-white/14 px-3 py-1.5 text-xs font-medium text-white/90 ring-1 ring-white/22">
|
||||
추천 시간 · {room.recommendedTime}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-white/58 px-3 py-1.5 text-xs font-medium text-brand-dark/84 ring-1 ring-brand-dark/12">
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-white/14 px-3 py-1.5 text-xs font-medium text-white/90 ring-1 ring-white/22">
|
||||
지금 분위기 · {room.vibeLabel}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user