feat: 로드나인 프로모션 - 제플린 가이드
This commit is contained in:
353
l9/promotion/zeplin-promotion.md
Normal file
353
l9/promotion/zeplin-promotion.md
Normal file
@@ -0,0 +1,353 @@
|
||||
# 프로모션 - 기본 템플릿 Zeplin MCP 마크업 작업 가이드
|
||||
|
||||
이 문서는 Zeplin MCP를 활용한 프로모션 페이지 마크업 작업 방식을 정리한 가이드입니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 환경 설정 (최초 1회)
|
||||
|
||||
### Zeplin MCP SSL 인증서 오류 해결
|
||||
|
||||
사내 네트워크/VPN 환경에서 Zeplin MCP가 `unable to get local issuer certificate` 오류를 발생시키는 경우, `~/.cursor/mcp.json`의 Zeplin 서버 `env`에 아래 항목을 추가해야 합니다.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"zeplin": {
|
||||
"timeout": 600,
|
||||
"command": "npx",
|
||||
"args": ["-y", "@zeplin/mcp-server@latest"],
|
||||
"env": {
|
||||
"ZEPLIN_ACCESS_TOKEN": "...",
|
||||
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
설정 변경 후 **Cursor 재시작** 필요 (`Cmd+Shift+P` → `Developer: Reload Window`).
|
||||
|
||||
> `NODE_TLS_REJECT_UNAUTHORIZED=0`은 로컬 Cursor MCP 전용 설정이며 소스코드/배포 환경과 완전히 분리됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 프로젝트 구조
|
||||
|
||||
```
|
||||
l9-fe/
|
||||
├── pages/promotion/{프로젝트명}/{날짜}/index.vue
|
||||
├── assets/scss/pages/promotion/{프로젝트명}/{날짜}/index.scss
|
||||
└── public/assets/promotion/{프로젝트명}/{날짜}/
|
||||
├── pc/
|
||||
│ ├── bg_section01_ko.jpg
|
||||
│ ├── bg_section01_tw.jpg
|
||||
│ └── ...
|
||||
└── mobile/
|
||||
├── bg_section01.jpg ← 기본 BG (언어 무관, 배경 전용 화면에서 다운로드)
|
||||
├── bg_section01_ko.jpg ← KO 콘텐츠 이미지 (KO 콘텐츠 화면에서 다운로드)
|
||||
├── bg_section01_tw.jpg ← TW 콘텐츠 이미지 (TW 콘텐츠 화면에서 다운로드)
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Vue 파일 주요 구조
|
||||
|
||||
- `promotion-content` 클래스: 프로젝트별 고유 클래스명 사용 (예: `dinapheri-update`)
|
||||
- 섹션 구성: `section01` ~ `sectionN`
|
||||
- 번역 키: `tm()` 함수로 호출, JSON 파일에서 관리
|
||||
- SCSS import: `<style>` 블록에서 `@import '@/assets/scss/pages/promotion/.../index.scss'`
|
||||
|
||||
---
|
||||
|
||||
## 3. Zeplin 화면 종류와 역할
|
||||
|
||||
프로모션 페이지는 용도별로 여러 Zeplin 화면이 존재합니다. 작업 전에 각 화면의 역할을 먼저 파악하세요.
|
||||
|
||||
### PC 화면
|
||||
|
||||
| 화면 역할 | 내용 |
|
||||
|----------|------|
|
||||
| PC KR 배경+콘텐츠 | PC용 전체 화면. 섹션 배경 이미지 + 텍스트 레이어 위치 좌표 확인용 |
|
||||
| PC TW 배경+콘텐츠 | TW 언어용 PC 전체 화면 |
|
||||
|
||||
### 모바일 화면 (3종)
|
||||
|
||||
모바일은 PC와 달리 **역할이 분리된 화면 3종**을 사용합니다.
|
||||
|
||||
| 화면 역할 | 파일명 규칙 | 설명 |
|
||||
|----------|-----------|------|
|
||||
| 모바일 BG 전용 (고해상도) | `bg_sectionXX.jpg` | 배경 이미지만 있는 화면. 보통 2048px 너비. `.view-mobile section`의 `background-image`에 사용 |
|
||||
| 모바일 KO 콘텐츠 | `bg_sectionXX_ko.jpg` | 배경 + KO 콘텐츠가 합성된 화면. `[lang='ko-KR'] section::before`의 `background-image`에 사용 |
|
||||
| 모바일 TW 콘텐츠 | `bg_sectionXX_tw.jpg` | 배경 + TW 콘텐츠가 합성된 화면. `[lang='zh-TW'] section::before`의 `background-image`에 사용 |
|
||||
|
||||
> **섹션 1개당 이미지 3개** 사용: `bg_sectionXX.jpg` + `bg_sectionXX_ko.jpg` + `bg_sectionXX_tw.jpg`
|
||||
|
||||
---
|
||||
|
||||
## 4. Zeplin MCP 사용법
|
||||
|
||||
### 화면 레이어 조회
|
||||
|
||||
```
|
||||
get_screen(url, includeVariants: false)
|
||||
```
|
||||
|
||||
조회 결과에서 확인할 항목:
|
||||
- 각 섹션 그룹 레이어의 `sourceId`, `rect.y`, `rect.height`
|
||||
- 텍스트 레이어의 절대 `y` 좌표 (notice, even-date 위치 계산용)
|
||||
- `exportable: true` 여부 → false이면 다운로드 불가 (디자이너에게 export 설정 요청)
|
||||
|
||||
### 레이어 구조 파악 방법
|
||||
|
||||
`get_screen`으로 조회하면 최상위 레이어 목록과 각 레이어의 `y`, `height` 값을 확인할 수 있습니다.
|
||||
섹션별 절대 y 시작값과 높이는 **프로젝트마다 다르므로**, 작업 전 반드시 전체 화면을 조회해서 직접 확인하세요.
|
||||
|
||||
```
|
||||
예시)
|
||||
레이어명 섹션 절대 y 시작 높이
|
||||
00_main → section01 0 1020
|
||||
01_con → section02 1020 2449
|
||||
02_con → section03 3469 1839
|
||||
...
|
||||
```
|
||||
|
||||
### 요소 위치 계산 공식
|
||||
|
||||
**PC** - CSS `top` (px 고정값):
|
||||
```
|
||||
CSS top = 텍스트 레이어 절대 y − 해당 섹션 절대 y 시작값
|
||||
|
||||
예시) section03의 notice 텍스트
|
||||
절대 y: 2830 − section03 시작 y: 1923 = top: 907px
|
||||
```
|
||||
|
||||
**모바일** - `toPercentage(섹션높이, 상대y)`:
|
||||
```
|
||||
상대 y = 텍스트 레이어 절대 y − 해당 섹션 절대 y 시작값
|
||||
CSS top = toPercentage(섹션높이, 상대y)
|
||||
|
||||
예시) section03의 even-date (섹션 y: 3469, 섹션 높이: 1839)
|
||||
텍스트 절대 y: 3697
|
||||
상대 y: 3697 - 3469 = 228
|
||||
→ toPercentage(1839, 228)
|
||||
```
|
||||
|
||||
> 모바일 텍스트 좌표는 **콘텐츠 화면(KO/TW)**에서 읽어야 정확합니다. BG 전용 화면에는 텍스트 레이어가 없습니다.
|
||||
|
||||
### 이미지 에셋 다운로드
|
||||
|
||||
```
|
||||
download_layer_asset(layerSourceId, localPath, assetType)
|
||||
```
|
||||
|
||||
- 다운로드 시 **UUID 파일명**으로 저장되므로 반드시 rename 필요
|
||||
- `exportable: false`인 레이어는 다운로드 불가 → 디자이너에게 Zeplin export 설정 요청 후 재시도
|
||||
- Zeplin 디자인이 수정되면 `sourceId`가 바뀔 수 있으므로 교체 시 화면 재조회 필수
|
||||
|
||||
---
|
||||
|
||||
## 5. SCSS 작성 규칙
|
||||
|
||||
### 전체 구조
|
||||
|
||||
```scss
|
||||
/* ─── PC ─────────────────────────────── */
|
||||
.view-desktop {
|
||||
.promotion-content.{프로젝트클래스} {
|
||||
section { /* 공통 PC 스타일 */ }
|
||||
.notice-list { /* 공통 */ }
|
||||
|
||||
.section01 { height: 826px; }
|
||||
.section02 {
|
||||
height: 1100px;
|
||||
}
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── 모바일 ──────────────────────────── */
|
||||
.view-mobile {
|
||||
.promotion-content.{프로젝트클래스} {
|
||||
section {
|
||||
position: relative;
|
||||
background-position: 50% 0;
|
||||
background-size: auto 100%;
|
||||
background-repeat: no-repeat;
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
max-width: toRem(750);
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
background: 50% 0 / 100% auto no-repeat;
|
||||
}
|
||||
.notice-list { /* 공통 모바일 notice-list 스타일 */ }
|
||||
.even-date { /* 공통 모바일 even-date 스타일 */ }
|
||||
}
|
||||
|
||||
/* 섹션별 개별 설정 */
|
||||
.section01 {
|
||||
background-image: url('/assets/.../mobile/bg_section01.jpg'); /* 기본 BG */
|
||||
&::before { @include inputMobileHeight(1020); } /* 섹션 높이값 */
|
||||
}
|
||||
.section02 {
|
||||
background-image: url('/assets/.../mobile/bg_section02.jpg');
|
||||
&::before { @include inputMobileHeight(2449); }
|
||||
.notice-list { top: toPercentage(2449, 2319); }
|
||||
}
|
||||
.section03 {
|
||||
background-image: url('/assets/.../mobile/bg_section03.jpg');
|
||||
&::before { @include inputMobileHeight(1839); }
|
||||
.even-date { top: toPercentage(1839, 228); }
|
||||
.notice-list { top: toPercentage(1839, 1673); }
|
||||
}
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── 언어별 이미지 ────────────────────── */
|
||||
[lang='ko-KR'] {
|
||||
.view-desktop .promotion-content.{프로젝트클래스} {
|
||||
.section01 { background-image: url('/assets/.../pc/bg_section01_ko.jpg'); }
|
||||
/* ... */
|
||||
}
|
||||
.view-mobile .promotion-content.{프로젝트클래스} {
|
||||
/* ::before 에 KO 콘텐츠 이미지 적용 */
|
||||
.section01::before { background-image: url('/assets/.../mobile/bg_section01_ko.jpg'); }
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
|
||||
[lang='zh-TW'] {
|
||||
.view-desktop .promotion-content.{프로젝트클래스} {
|
||||
.section01 { background-image: url('/assets/.../pc/bg_section01_tw.jpg'); }
|
||||
/* ... */
|
||||
}
|
||||
.view-mobile .promotion-content.{프로젝트클래스} {
|
||||
.section01::before { background-image: url('/assets/.../mobile/bg_section01_tw.jpg'); }
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 모바일 이미지 레이어링 원리 (모바일 전용)
|
||||
|
||||
> 아래 구조는 **모바일에만 해당**합니다. PC는 section에 배경 이미지 하나만 사용합니다.
|
||||
|
||||
```
|
||||
[section 요소 자체]
|
||||
background-image: bg_sectionXX.jpg
|
||||
→ 화면 전체를 채우는 배경 깔판 (언어 무관)
|
||||
|
||||
[section::before 요소] ← section 위에 겹쳐지는 콘텐츠 레이어
|
||||
background-image: bg_sectionXX_ko.jpg ([lang='ko-KR'] 블록)
|
||||
or bg_sectionXX_tw.jpg ([lang='zh-TW'] 블록)
|
||||
→ max-width: 750px, margin: 0 auto 로 중앙 정렬
|
||||
→ padding-top % 로 이미지 비율 유지 (inputMobileHeight mixin)
|
||||
→ 배경 위에 KO/TW 콘텐츠 이미지가 가운데 자리잡음
|
||||
```
|
||||
|
||||
즉, 배경(bg_sectionXX.jpg)은 section에, 언어별 콘텐츠 이미지(bg_sectionXX_ko/tw.jpg)는 ::before에 분리해서 적용합니다.
|
||||
|
||||
**PC는 section에 언어별 배경 이미지 하나만 사용** (`[lang] section { background-image: bg_sectionXX_ko.jpg }`).
|
||||
|
||||
### inputMobileHeight 값
|
||||
|
||||
`inputMobileHeight(N)` 의 `N`은 **Zeplin BG 전용 화면의 섹션 높이(px)**에서 읽어옵니다.
|
||||
콘텐츠 화면과 BG 화면의 섹션 높이는 동일하므로 어느 쪽에서 읽어도 무방합니다.
|
||||
|
||||
### 공통 요소 스타일
|
||||
|
||||
| 요소 | PC 주요 스타일 | 모바일 주요 스타일 |
|
||||
|------|-------------|----------------|
|
||||
| `.notice-list ul li` | `font-size: 16px`, `line-height: 26px`, `color: #978c7a` | `font-size: toRem(10)`, `padding-left: toRem(12)` |
|
||||
| `.notice-list li::before` | `content: '•'`, `position: absolute`, `left: 0`, `top: 50%`, `transform: translateY(-50%)` | 동일 |
|
||||
| `.notice-link` | `font-weight: 600`, `color: #b08442`, `text-decoration: underline` | 동일 |
|
||||
| `.even-date` | `font-size: 20px`, `color: rgba(156,148,132,0.97)` | `font-size: rem(12)`, `letter-spacing: -0.8px` |
|
||||
|
||||
---
|
||||
|
||||
## 6. 마크업 패턴
|
||||
|
||||
### 기본 섹션 구조 (section01 참고)
|
||||
|
||||
```vue
|
||||
<section id="section01" class="section01">
|
||||
<h3 class="blind">
|
||||
<span>{{ tm('ProjectName_Section01_SubTitle') }}</span>
|
||||
<em>{{ tm('ProjectName_Section01_Title') }}</em>
|
||||
</h3>
|
||||
<p class="blind">{{ tm('ProjectName_Section01_Desc') }}</p>
|
||||
<button type="button" class="btn-partner"
|
||||
@click="[sendLog('event-name', 'UI-1'), goToLink(tm('ProjectName_Section01_Btn_Url'), '_blank')]">
|
||||
<span class="blind">{{ tm('ProjectName_Section01_Btn') }}</span>
|
||||
</button>
|
||||
</section>
|
||||
```
|
||||
|
||||
### 이벤트 섹션 구조 (notice-list + even-date 패턴)
|
||||
|
||||
```vue
|
||||
<section id="section03" class="section03">
|
||||
<h4 class="blind">{{ tm('ProjectName_Section03_Title') }}</h4>
|
||||
<p class="blind">{{ tm('ProjectName_Section03_Event_Rewards') }}</p>
|
||||
<p class="even-date">{{ tm('ProjectName_Section03_Date') }}</p>
|
||||
<div class="notice-list">
|
||||
<ul>
|
||||
<template v-for="(notice, noticeIdx) in tm('ProjectName_Section03_Notice_List')" :key="noticeIdx">
|
||||
<!-- 마지막 항목에만 링크 포함 -->
|
||||
<li v-if="noticeIdx === tm('ProjectName_Section03_Notice_List').length - 1">
|
||||
<span v-dompurify-html="tm(`${notice}`)"></span>
|
||||
<span v-dompurify-html="tm(`${notice}_Link`)"
|
||||
class="notice-link"
|
||||
@click="sendLog('event-name', 'UI-XX')">
|
||||
</span>
|
||||
</li>
|
||||
<li v-else v-dompurify-html="tm(`${notice}`)"></li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 작업 순서 (새 세션 시작 시)
|
||||
|
||||
### PC 작업
|
||||
|
||||
1. 이 문서 숙지
|
||||
2. PC KR 화면 `get_screen` 조회 → 섹션별 레이어 y, height, sourceId 확인
|
||||
3. 각 섹션 텍스트 레이어 절대 y → 상대 y 계산 (`top: px`)
|
||||
4. PC KR 이미지 다운로드 → `bg_sectionXX_ko.jpg`로 rename
|
||||
5. PC TW 화면 조회 → TW 이미지 다운로드 → `bg_sectionXX_tw.jpg`로 rename
|
||||
6. Vue 마크업 작성
|
||||
7. SCSS PC 스타일 작성 → 언어별 이미지 경로 블록 추가
|
||||
|
||||
### 모바일 추가 작업
|
||||
|
||||
8. **모바일 BG 전용 화면** 조회 → 섹션 높이(`inputMobileHeight` 값) 확인
|
||||
- 이미지 다운로드 → `bg_sectionXX.jpg`로 rename
|
||||
9. **모바일 KO 콘텐츠 화면** 조회 → 텍스트 레이어 절대 y 확인
|
||||
- `상대 y = 텍스트 절대 y − 섹션 시작 y` 계산
|
||||
- `toPercentage(섹션높이, 상대y)` 값 SCSS에 적용
|
||||
- 이미지 다운로드 → `bg_sectionXX_ko.jpg`로 rename
|
||||
10. **모바일 TW 콘텐츠 화면** 조회 (필요 시)
|
||||
- 이미지 다운로드 → `bg_sectionXX_tw.jpg`로 rename
|
||||
11. SCSS 모바일 스타일 작성:
|
||||
- `.view-mobile section` 공통 스타일
|
||||
- 섹션별 `background-image`, `inputMobileHeight`, `toPercentage` 값
|
||||
- `[lang='ko-KR']` 블록: `section::before { background-image: ..._ko.jpg }`
|
||||
- `[lang='zh-TW']` 블록: `section::before { background-image: ..._tw.jpg }`
|
||||
|
||||
---
|
||||
|
||||
## 8. 주의사항
|
||||
|
||||
- **`exportable: false` 레이어는 다운로드 불가** → 디자이너에게 Zeplin에서 export 설정 요청 후 재시도
|
||||
- **Zeplin 디자인 수정 시 sourceId 변경 가능** → 이미지 교체 시 반드시 화면 재조회
|
||||
- **모바일 텍스트 좌표는 콘텐츠 화면에서 읽을 것** → BG 전용 화면에는 텍스트 레이어 없음
|
||||
- **`inputMobileHeight` 값 = BG 화면의 섹션 height** → 콘텐츠 화면과 동일하므로 어느 쪽이든 무방
|
||||
- **번역 키**는 별도 JSON 파일에서 관리 (`tm()` 함수로 호출)
|
||||
- **`blind` 클래스**는 스크린리더 전용 숨김 텍스트
|
||||
- SCSS mixin: `inputMobileHeight`, `inputMobileSize`, `toPercentage`, `toRem`, `rem` 등은 프로젝트 공통 정의 사용
|
||||
Reference in New Issue
Block a user