Skip to content

Latest commit

 

History

History
364 lines (291 loc) · 11 KB

File metadata and controls

364 lines (291 loc) · 11 KB

보드 프로젝트 진행 정리


1단계: 프로젝트 생성

npx create-next-app@latest board --js --tailwind --eslint --app --src-dir --import-alias "@/*"
옵션 의미
npx 도구를 임시로 실행 (설치 안 함)
create-next-app@latest Next.js 프로젝트 생성 도구 (최신 버전)
board 프로젝트 폴더 이름
--js JavaScript 사용 (TypeScript 아님)
--tailwind Tailwind CSS 포함
--eslint ESLint 포함 (코드 규칙 검사 도구)
--app App Router 사용 (Next.js의 라우팅 방식)
--src-dir src/ 폴더 구조 사용
--import-alias "@/*" @/로 src 폴더를 참조할 수 있게 설정

생성된 폴더 구조:

board/
├── src/
│   └── app/
│       ├── layout.js      ← 전체 페이지 틀 (HTML 구조)
│       ├── page.js         ← 메인 페이지 (화면에 보이는 내용)
│       └── globals.css     ← 전역 스타일
├── package.json            ← 프로젝트 정보 + 설치된 패키지 목록
└── ...

2단계: Git 브랜치 생성

git branch chore/VEASLY-365-nextjs-프로젝트-생성     # 브랜치 생성
git checkout chore/VEASLY-365-nextjs-프로젝트-생성    # 브랜치 전환
명령어 의미
git branch 이름 새 브랜치 생성 (전환은 안 함)
git checkout 이름 해당 브랜치로 전환
git checkout -b 이름 생성 + 전환을 한 번에

3단계: 보일러플레이트 정리

3-1. layout.js 수정 (metadata 변경)

// 변경 전
export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

// 변경 후
export const metadata = {
  title: "Board",
  description: "게시판",
};

설명:

  • metadata — 브라우저 탭 제목(title)과 검색엔진 설명(description)을 설정
  • export const — 이 변수를 다른 파일(Next.js 시스템)에서 사용할 수 있게 내보냄

3-2. layout.js 수정 (불필요한 폰트 import 제거)

// 변경 전
import { Geist, Geist_Mono } from "next/font/google";

const geist = Geist({ ... });
const geistMono = Geist_Mono({ ... });

// 변경 후
// (삭제함 — 기본 폰트 사용)

설명:

  • import — 다른 파일/패키지에서 기능을 가져오는 문법
  • Geist 폰트는 Next.js가 기본으로 넣어준 샘플이라 제거

3-3. page.js 보일러플레이트 제거

// 변경 전: Next.js 로고, 안내 문구 등 샘플 코드
// 변경 후: 깔끔한 빈 페이지

3-4. globals.css 다크모드 제거

/* 변경 전: 다크모드 블록 포함 */
@media (prefers-color-scheme: dark) {
  :root {
    --background: #0a0a0a;
    --foreground: #ededed;
  }
}

/* 변경 후: 다크모드 블록 삭제 */

설명:

  • --background — CSS 변수, 배경색
  • --foreground — CSS 변수, 글자색
  • @media (prefers-color-scheme: dark) — 사용자 기기가 다크모드일 때 적용되는 스타일

4단계: 첫 커밋

git add src/app/layout.js src/app/page.js    # 스테이징 (커밋할 파일 선택)
git commit -m "[VEASLY-367] 보일러플레이트 정리"  # 커밋 (변경 이력 저장)
명령어 의미
git add 파일 커밋에 포함할 파일을 선택 (스테이징)
git commit -m "메시지" 선택된 파일들을 메시지와 함께 저장
-m message의 약자, 커밋 메시지를 바로 입력
git add src/app/globals.css
git commit -m "[VEASLY-367] 다크모드 보일러플레이트 제거"

5단계: 메모 보드 CRUD UI 개발

새 브랜치 생성:

git checkout main                                    # main으로 전환
git merge chore/VEASLY-365-nextjs-프로젝트-생성       # 이전 작업을 main에 합침
git checkout -b feature/VEASLY-389-메인보드-UI생성     # 새 브랜치 생성 + 전환

page.js 전체 코드 (최종 완성본):

"use client";

import { useState } from "react";

export default function Home() {
  const [memos, setMemos] = useState([]);
  const [input, setInput] = useState("");
  const [editingId, setEditingId] = useState(null);
  const [editingText, setEditingText] = useState("");

  const addMemo = () => {
    if (!input.trim()) return;
    setMemos([...memos, { id: Date.now(), text: input }]);
    setInput("");
  };

  const deleteMemo = (id) => {
    setMemos(memos.filter((memo) => memo.id !== id));
  };

  const startEdit = (memo) => {
    setEditingId(memo.id);
    setEditingText(memo.text);
  };

  const saveEdit = (id) => {
    setMemos(
      memos.map((memo) =>
        memo.id === id ? { ...memo, text: editingText } : memo
      )
    );
    setEditingId(null);
    setEditingText("");
  };

  const cancelEdit = () => {
    setEditingId(null);
    setEditingText("");
  };

  return (
    <div className="max-w-xl mx-auto p-6">
      <h1 className="text-2xl font-bold mb-6">게시판</h1>

      <div className="flex gap-2 mb-6">
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="내용을 입력하세요"
          className="flex-1 border border-gray-300 rounded px-3 py-2"
        />
        <button
          onClick={addMemo}
          className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
        >
          추가
        </button>
      </div>

      <ul className="space-y-2">
        {memos.map((memo) => (
          <li key={memo.id} className="flex items-center gap-2 border border-gray-200 rounded px-3 py-2">
            {editingId === memo.id ? (
              <>
                <input
                  type="text"
                  value={editingText}
                  onChange={(e) => setEditingText(e.target.value)}
                  className="flex-1 border border-gray-300 rounded px-2 py-1"
                />
                <button onClick={() => saveEdit(memo.id)} className="text-green-600 hover:text-green-800">저장</button>
                <button onClick={cancelEdit} className="text-gray-500 hover:text-gray-700">취소</button>
              </>
            ) : (
              <>
                <span className="flex-1">{memo.text}</span>
                <button onClick={() => startEdit(memo)} className="text-blue-500 hover:text-blue-700">수정</button>
                <button onClick={() => deleteMemo(memo.id)} className="text-red-500 hover:text-red-700">삭제</button>
              </>
            )}
          </li>
        ))}
      </ul>
    </div>
  );
}

코드 전체 흐름 설명:

1. 선언부

"use client";
  • Next.js에게 "이 파일은 브라우저에서 실행해라"라고 알려줌
  • useState 같은 React 기능을 쓰려면 필수
import { useState } from "react";
  • React 라이브러리에서 useState 기능을 가져옴
  • { } 안에 이름을 적어서 필요한 것만 가져오는 문법 (named import)

2. 함수 정의

export default function Home() {
  • function Home() — 페이지를 그리는 함수
  • export default — Next.js가 이 함수를 페이지로 사용할 수 있게 내보냄

3. State (상태 관리)

const [memos, setMemos] = useState([]);       // 메모 목록
const [input, setInput] = useState("");        // 입력창 텍스트
const [editingId, setEditingId] = useState(null);    // 수정 중인 메모 id
const [editingText, setEditingText] = useState("");  // 수정 중인 텍스트
  • useState(초기값) — 값을 저장하고, 바뀌면 화면을 다시 그림
  • [현재값, 변경함수] — 읽기용과 쓰기용이 항상 쌍으로 나옴
  • [] — 빈 배열, "" — 빈 문자열, null — 값 없음

4. 함수들

함수 역할 핵심 문법
addMemo 메모 추가 [...기존, 새것] — 스프레드로 배열 합치기
deleteMemo 메모 삭제 .filter() — 조건에 맞는 것만 남기기
startEdit 수정 시작 state에 수정 대상 정보 저장
saveEdit 수정 저장 .map() + 삼항연산자로 해당 메모만 교체
cancelEdit 수정 취소 state 초기화

5. JSX (화면)

문법 의미
<div className="..."> HTML의 class 대신 className 사용 (JSX 규칙)
{변수} JavaScript 값을 화면에 표시
{조건 ? A : B} 삼항연산자 — 조건이 true면 A, false면 B
{배열.map((item) => ...)} 배열을 돌면서 각 항목을 JSX로 변환
<> </> Fragment — 여러 요소를 감싸는 빈 껍데기
onClick={함수} 클릭 이벤트 연결
onChange={(e) => ...} 입력 이벤트 연결, e.target.value로 입력값 가져옴

6단계: 커밋 + 머지

git add src/app/page.js
git commit -m "[VEASLY-389] 메모 보드 CRUD UI 구현"

git add src/app/page.js
git commit -m "[VEASLY-389] Tailwind CSS 스타일링 적용"

git checkout main
git merge feature/VEASLY-389-메인보드-UI생성

Git 명령어 총정리

명령어 의미
git branch 브랜치 목록 보기
git branch 이름 새 브랜치 생성
git branch -m 이름 현재 브랜치 이름 변경
git checkout 이름 브랜치 전환
git checkout -b 이름 생성 + 전환 동시에
git status 현재 상태 확인 (스테이징 여부 등)
git add 파일 스테이징 (커밋할 파일 선택)
git commit -m "메시지" 커밋 (변경 이력 저장)
git commit --amend -m "메시지" 마지막 커밋 메시지 수정
git merge 브랜치 다른 브랜치를 현재 브랜치에 합침
git log 커밋 이력 보기

현업 Git 워크플로우

티켓 생성 → 브랜치 생성 → 작업 → 커밋 → PR → 코드 리뷰 → main에 머지

브랜치 접두어

접두어 용도 예시
feature/ 새로운 기능 추가 feature/BOARD-1-게시판-목록
fix/ 버그 수정 fix/BOARD-5-로그인-오류
hotfix/ 긴급 버그 수정 hotfix/BOARD-10-결제-실패
refactor/ 기능 변경 없이 코드 개선 refactor/BOARD-7-API-정리
chore/ 설정, 세팅 등 잡무 chore/BOARD-3-eslint-설정

브랜치 구조

main (항상 배포 가능한 안정된 코드)
 ├── feature/게시판-목록  → 작업 완료 → PR → main에 머지
 ├── feature/게시글-작성  → 작업 완료 → PR → main에 머지
 └── fix/로그인-오류     → 작업 완료 → PR → main에 머지

티켓 구조

[VEASLY-365] Next.js + Tailwind 프로젝트 초기 셋업, Nest.js + Prisma + PostgreSQL 초기 셋업
 ├── [VEASLY-367] Next.js 16 App Router 프로젝트 생성 ✅
 ├── [VEASLY-387] Nest.js + Prisma + PostgreSQL 세팅 (다음 작업)
 └── [VEASLY-389] 메인보드 UI 작업 ✅