Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
6bd97b4
[v0.1.0] – 초기 레이아웃 및 테스트 자동화 기반 구축
swallowedB Nov 17, 2025
4d89523
[v0.2.0] - DockMenu UI 구현 및 구조 리팩토링 / 빌드 및 타입 체크 오류 해결
swallowedB Nov 18, 2025
d65a817
[v0.3.0] - 버전 픽스 및 스타일링 추가/수정
swallowedB Dec 17, 2025
456c344
[v0.3.1] - Giscus 커스텀 스타일링 1차 반영
swallowedB Dec 17, 2025
7eeab12
[v0.3.2] - Giscus 커스텀 스타일링 2차 반영
swallowedB Dec 17, 2025
f6d0d8e
[v0.3.3] - Giscus 커스텀 스타일링 3차 반영
swallowedB Dec 17, 2025
02b8193
[v0.4.0] - 카테고리 페이지 퍼블리싱 및 Velite 설정
swallowedB Dec 23, 2025
7bae39f
[v0.4.1] - Dock 인터랙션 추가 및 mdx 스타일링
swallowedB Jan 10, 2026
07fe971
[v0.5.0] - 포스트 상세 기능 및 검색 추가 & 404/fallback 처리
swallowedB Jan 14, 2026
6f2ecef
[v0.5.1] - Analytics 추가 및 시리즈 수 집계 문제 해결
swallowedB Jan 14, 2026
1bf63d1
[v0.5.2] - 추천 포스트 기능 및 인기순 정렬 추가 & UX 개선
swallowedB Jan 15, 2026
b8a5d6b
[v0.5.3] - cloudinary 설정 추가
swallowedB Jan 16, 2026
fa75feb
📎 post : plaist 블로그 글 이전
swallowedB Jan 16, 2026
a751626
🚨 Fix : 시리즈 미반영 문제 해결 및 모바일 반응형 고려
swallowedB Jan 16, 2026
bbe3ba7
📎 post : comma & roome 블로그 글 이전
swallowedB Jan 16, 2026
354ec1a
🚨 Fix : 폴더 정렬 스타일 조건부 추가
swallowedB Jan 16, 2026
da240b2
📎 post : 2025년 회고
swallowedB Jan 19, 2026
4129e56
✨ feat: 링크 컴포넌트 + 플러그인 추가
swallowedB Jan 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ tmp/

# --- OS / 기타 ---
Thumbs.db

# MDX 작성용 템플릿
content/_templates/
76 changes: 76 additions & 0 deletions content/posts/DEV_LOG/comma-0-background.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: "#0. 프로젝트 기획 및 회고"
slug: "comma-0-background"
date: "2025-03-13"
category: "Dev_log"
series: "comma"
tags: ["project", "background"]
summary: "Plaist의 아이디어 도출, 사용자 인사이트, 서비스 구조, 협업 회고를 포함한 프로젝트 초기 기획 기록"
thumbnail: "https://res.cloudinary.com/dvapam1ks/image/upload/v1768574588/2_turhvn.jpg"
draft: false
---
## 1. COMMA의 배경

### 📍 어떤 니즈가 있을까?
<Quote>
현대 사회는 고물가, 빠듯한 직장 생활, 치열한 학업 등으로 인해 많은 사람들이 스트레스를 해소할 공간을 찾고 있다. 스트레스 해소 방법을 고민하던 중, 문득 게임이 떠올랐다. 게임을 즐기는 짧은 순간에도 도파민이 분비되어 정서적 안정감을 주는 등, 게임은 훌륭한 스트레스 해소 수단이 될 수 있을 것 같았다.
</Quote>

<Quote>
또한 자료 조사에 따르면, 현대 사회에서 레트로 문화가 하나의 휴식 방법으로 자리 잡고 있으며, 과거의 경험을 재현하는 것이 심리적 위안을 준다는 분석이 나왔다.
</Quote>

### 📍 어떻게 도출되었을까?
- "바쁜 현대인들에게 잠깐의 여유를 제공하고, 어린 시절 오락실의 감성을 되살릴 수 있는 공간이 있다면 어떨까?" 하는 생각이 도출되었고
- 자연스럽게 `쉬다 → 쉼 → 쉼표` 로 이어져 "추억을 깨우는 즐거운 쉼표!" 의 COMMA가 탄생했다.
- COMMA는 향수를 자극하는 경험과 새로운 소셜 연결을 동시에 제공하는 미니게임 & 커뮤니티 플랫폼으로,
- 누구나 쉽게 접근할 수 있는 미니게임으로 짧은 시간 동안 스트레스를 해소하고 성취감을 느낄 수 있다.

---

## 2. COMMA의 기획

### 📍 주요 기능 및 게임 종류
- 소셜 로그인
- 전광판
- 게임 커뮤니티
- 게임 (테트리스 / 플래피부 / 바운스볼 / 슈팅게임 / 지뢰찾기)
- 자유 커뮤니티
- 게시글 작성/수정
- 댓글
- 랭킹
- 마이페이지

### 📍 IA
<Quote>
서비스의 전반적인 기능과 메뉴 구조를 볼 수 있게 작성하였다.
</Quote>

<Figure
images={[
{
src: "https://res.cloudinary.com/dvapam1ks/image/upload/v1768574874/image_cimchi.png",
alt: "COMMA IA 구조",
},
]}
caption="서비스 IA 구조"
/>

---

## 3. KPT 회고

<Callout type="info" icon="🚀" title="Keep">
- 퍼블리싱이 확실히 익숙해지고 빨라졌다는 것을 느낄 수 있었다. 다른 팀원분들 것도 도와주면서 더 많이 익숙해진 것 같다.
- 가장 뿌듯했던 건 이번에 처음으로 라이브러리를 활용해보았는데, 라이브러리는 이렇게 활용하고 사용하는 거구나를 알 수 있는 계기가
</Callout>

<Callout type="info" icon="🚨" title="Problem">
- 가장 아쉬웠던 점은 `supabase` 를 공부하고 싶었는데, 그렇게 하지 못했다는 점이다. 아무래도 기간이 3주밖에 없어서 잘 - 아는 팀원이 붙어서 빠르게 작업을 할 수밖에 없었다. 하지만 배워가는 시기인데, 한 번 무모하게 도전을 해봐도 좋았을 거라는 생각이 든다.
- 그래서 이번 기회에 짜여져있는 것들을 참고하면서 `supabase` 에 대해서 좀 더 공부해보려고 한다.
</Callout>

<Callout type="info" icon="🔥" title="Try">
- 이번 프로젝트는 `Vue` 로 진행했는데, 이걸 다시 `React`로 마이그레이션 해보려고 한다.
- 또한 `Supabase` 백업본을 받아서 이걸 옮기는 작업도 해보면서 한번 유지 관리를 해보려고 한다.
</Callout>
212 changes: 212 additions & 0 deletions content/posts/DEV_LOG/comma-1-imageupload.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
---
title: "#1. 다중 이미지 업로드 구현"
slug: "comma-1-imageupload"
date: "2025-03-26"
category: "Dev_log"
series: "comma"
tags: ["project", "vue"]
summary: "Vue 기반 다중 이미지 업로드 기능 개발 중 발생한 문제와 해결 과정"
thumbnail: "https://res.cloudinary.com/dvapam1ks/image/upload/v1768575993/image_rhw8oa.jpg"
draft: false
---

<Figure
images={[
{
src: "https://res.cloudinary.com/dvapam1ks/image/upload/v1768575993/image_rhw8oa.jpg",
alt: "다중 이미지 구현",
},
]}
/>

## 💬 구성 흐름

<Quote>
파일 선택 → 미리보기 생성 → 상위 컴포넌트로 데이터 전달 → 서버 업로드 준비 → 업로드
</Quote>

---

## 🔎 컴포넌트 구조

### 📍 PostEditImg (부모)
- 다중 이미지 업로드 UI와 로직 담당
- 최대 4개의 이미지 슬롯 관리
- 업로드 트리거 및 미리보기 생성

### 📍 PostEditImgCard (자식)
- 개별 이미지 카드 렌더링
- 데이터 존재 여부에 따라 미리보기 or 업로드 버튼 표시
- size / opacity 등 props 기반 스타일링

<Quote>
input[type="file"]는 숨기고 카드 클릭으로 input을 트리거하는 방식
</Quote>

---

## 🔗 임시 URL 생성

- `URL.createObjectURL(file)` → 브라우저 미리보기 URL 생성
- `<img :src="preview">` 바로 표현 가능

### ✔ 메모리 해제 필요

- createObjectURL은 자동 해제되지 않음
- 사용 후 `URL.revokeObjectURL(url)` 호출 필수

#### 수정 전

```js
handleFileChange(event, index) {
const files = event.target.files;
if (!files || files.length === 0) return;

const newImages = Array.from(files).map((file) => ({
file,
preview: URL.createObjectURL(file),
}));

this.$emit("addImage", { index, images: newImages });
event.target.value = "";
}
```

#### 수정 후

```js
handleFileChange(event, index) {
const files = event.target.files;
if (!files || files.length === 0) return;

if (this.images[index] && this.images[index].preview) {
URL.revokeObjectURL(this.images[index].preview);
}

const newImages = Array.from(files).map((file) => ({
file,
preview: URL.createObjectURL(file),
}));

this.$emit("addImage", { index, images: newImages });
event.target.value = "";
}
```

### 💬 코멘트

<Quote>
미리보기 구현은 쉬운데, 메모리 해제를 고려하지 않으면 장시간 사용 시 누수 발생 가능
</Quote>

---

## 🚨 이미지 추가 시 슬롯 위치 벗어남

<Figure
images={[
{
src: "https://res.cloudinary.com/dvapam1ks/image/upload/v1768576018/image_idcg0u.png",
alt: "슬롯 위치 오류",
},
]}
/>

### 문제 상황

<Quote>
이미지 추가 시 0번 슬롯에 위치해야 하지만, 조건 분기 불명확으로 인해 예상치 못한 레이아웃이 발생
</Quote>

### 관련 코드

```vue
<post-edit-img-card
v-if="images.length > 0"
:imgSrc="images[0].preview || images[0]"
:size="'w-[400px] h-[400px] lg:w-[440px] lg:h-[440px]'"
:opacity="100"
@click="handleRemoveImage(0)"
/>
```

---

## 💡 원인 분석

<Quote>
- Vue는 Virtual DOM 기반으로 컴포넌트 재사용을 시도함
- v-if만 존재하고 v-else가 없으면 상태 변화 시 렌더링 우선순위가 흔들릴 수 있음
- 커스텀 컴포넌트 재사용 시 슬롯 위치가 어긋나는 문제 발생
</Quote>

---

## ✅ 해결 방법

### 1. v-else 명시

```vue
<post-edit-img-card
v-if="images.length > 0"
:imgSrc="images[0].preview"
@click="handleRemoveImage(0)"
/>
<post-edit-img-card
v-else
@click="triggerFileSelect(0)"
/>
```

<Quote>
분기 조건이 명확해져서 Virtual DOM이 정상적으로 슬롯을 유지
</Quote>

---

### 2. key 고정값 부여

```vue
<post-edit-img-card
v-if="images.length > 0"
:key="'image-' + images[0].preview"
:imgSrc="images[0].preview"
/>
```

<Quote>
key는 Vue가 컴포넌트를 비교할 때 식별자로 활용됨 → 위치 안정화
</Quote>

---

## 📍 최종 구현 패턴

```vue
<!-- 0번 슬롯 -->
<post-edit-img-card
v-if="images.length > 0"
:imgSrc="images[0].preview"
:size="'w-[400px] h-[400px] lg:w-[440px] lg:h-[440px]'"
:opacity="100"
@click="handleRemoveImage(0)"
/>
<post-edit-img-card
v-else
@click="triggerFileSelect(0)"
/>
```

<Quote>
업로드 모드 + 수정 모드 모두 지원 가능하도록 설계
</Quote>

---

## 🛠️ 개선 아이디어

- 드래그 앤 드랍 업로드
- Remove/Insert 시 애니메이션 추가
- 용량 및 파일 확장자 유효성 검사
- Multi 업로드 + Progress 표시
- 서버 업로드 큐 관리
Loading
Loading