"개념은 이해했는데, 막상 프로젝트 시작하려니까 뭐부터 말해야 할지 모르겠어요. 그냥 '할 일 관리 앱 만들어줘' 하면 되나요?"
맞아요. 처음엔 다들 그래요. 개념을 배우고 나서도 실전에서 어떻게 대화해야 할지 막막하죠.
실제로 "할 일 관리 앱 만들어줘" 하면 어떻게 될까요?
처음엔 뭔가 나와요. 할 일 추가하고, 삭제하고, 완료 체크하는 기본 기능이 만들어져요.
근데 여기서 기능을 하나 더 추가하려고 하면 꼬이기 시작해요.
"카테고리 기능 추가해줘" → 어? 기존 할 일들이랑 연결이 안 되네? "공유 기능 넣어줘" → 어? 권한 관리가 없어서 다 보이네?
왜 이런 일이 생길까요?
처음에 구조를 잡지 않고 만들었기 때문이에요. "일단 만들어줘" 하면 AI가 최소한으로만 만들어요. 나중에 확장할 구조는 없어요.
PART 4에서 개념을 따로따로 배웠죠?
- 16편: 자료구조 - "이건 트리 구조로 관리해줘"
- 17편: 아키텍처 - "레이어드 아키텍처로 가되, 서비스 레이어 분리해줘"
- 18편: 디자인 패턴 - "이 컴포넌트는 Container-Presenter 패턴으로 분리해줘"
- 19편: 상태 관리 - "로그인 상태는 전역으로, 폼 데이터는 로컬로 관리해줘"
각각은 알겠는데, 이걸 어떻게 조합해서 AI한테 말해야 할지 감이 안 오는 거예요.
이제 이걸 하나로 합쳐야 해요.
차이를 만드는 건 "어떻게 시키느냐"예요.
오늘은 이 모든 개념을 조합해서 처음부터 끝까지 AI와 대화하는 법을 보여드릴게요. 할 일 관리 앱을 설계부터 구현까지 실제로 만들면서요.
중요한 건, AI한테 한 번에 다 말하는 게 아니에요.
단계별로 대화하면서 구조를 잡아가는 거예요. "일단 시킨다 → 확인한다 → 수정 요청한다 → 다음 단계로" 이 흐름이요.
이 글을 읽고 나면
- PART 4 개념들을 어떻게 조합하는지 알 수 있어요
- 프로젝트 시작할 때 AI랑 어떻게 대화하는지 감을 잡을 수 있어요
- 단계별로 뭘 먼저 정하고, 뭘 나중에 정하는지 알 수 있어요
- "구조 잡아줘" → "이 패턴으로 해줘"라고 말할 수 있어요
오늘 만들 것: 할 일 관리 앱
목표: PART 4 개념을 총동원해서 설계 지시하기
✅ 기능
- 할 일 추가, 완료, 삭제
- 로그인/회원가입
- 다른 기기에서도 동기화
🎯 PART 4 개념 적용
- 자료구조: todos는 배열로 관리
- 아키텍처: 레이어드 (UI / 비즈니스 / 데이터)
- 디자인 패턴: Container-Presenter, 싱글톤(로그인 상태)
- 상태 관리: 전역(로그인) + DB(할 일 데이터)
🛠️ 기술 스택
- Next.js (프론트엔드)
- Supabase (백엔드/DB/인증)
- zustand (전역 상태 관리) 복잡해 보이죠? 근데 이걸 AI랑 대화하면서 단계적으로 만들면 돼요.
대화 흐름 전체 보기
실전 대화를 보기 전에, 전체 흐름을 먼저 보여드릴게요.

1단계: 기술 스택 결정
→ 뭘로 만들지 정하기
→ Next.js, Supabase, zustand
2단계: 아키텍처 결정
→ 전체 구조 어떻게 잡을지
→ 레이어드 아키텍처 (UI / 서비스 / 데이터)
3단계: 데이터 설계
→ 어떤 데이터를 어디에 저장할지
→ 자료구조 + 상태 관리 조합
4단계: 컴포넌트 구조 설계
→ UI를 어떻게 나눌지
→ Container-Presenter 패턴 적용
5단계: 구현 및 확인
→ 만들고 → 확인하고 → 피드백
→ 기능 단위로 테스트
💡 핵심: 한 번에 다 말하는 게 아니라 단계적으로! 이제 실전 대화를 보여드릴게요.
실전 대화: 1단계 - 기술 스택 결정
첫 대화는 뭘로 만들지 정하는 거예요.
┌───────────────────────────────────────────────────────────────────────┐ │ 1단계: 기술 스택 결정 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ 나: "할 일 관리 웹앱 만들 건데, 다른 기기에서도 동기화되어야 해. │ │ Next.js로 만들고, 백엔드는 Supabase 쓸 거야. │ │ 인증(로그인)도 Supabase Auth로 갈게." │ │ │ │ AI: "좋습니다. 상태 관리는 어떻게 하시겠어요? │ │ (상태 관리 = 앱 전체에서 데이터 공유하는 방법이에요) │ │ │ │ 옵션: │ │ 1. React Context │ │ → Next.js에 기본으로 있어서 설치 안 해도 됨 │ │ → 근데 데이터 많아지면 코드가 복잡해짐 │ │ │ │ 2. zustand ⭐ 추천 │ │ → 설치는 해야 하지만 코드가 엄청 짧음 │ │ → 배우기 쉽고, 작은~중간 규모에 딱 맞음 │ │ │ │ 3. Redux │ │ → 대기업 큰 프로젝트용. 배울 게 많음 │ │ → 할 일 앱에는 과한 선택 │ │ │ │ 할 일 관리 앱 규모면 zustand 추천드려요. │ │ 코드 적게 쓰고, 나중에 기능 추가해도 관리하기 편해요." │ │ │ │ 나: "zustand로 가자. │ │ 네 말대로 규모 작으니까 간단한 게 좋겠어. │ │ Redux는 배울 게 많다니까 나중에 필요하면 그때 배울게." │ │ │ │ AI: "기술 스택 정리: │ │ - Frontend: Next.js │ │ - Backend/DB: Supabase │ │ - Auth: Supabase Auth │ │ - State: zustand │ │ │ │ 프로젝트 생성부터 시작할까요?" │ │ │ │ 나: "아직 안 돼. 구조부터 정하자." │ │ │ └───────────────────────────────────────────────────────────────────────┘
이 대화에서 뭘 했나요?
- 도구만 정했어요. 구조는 아직 안 정했어요.
- AI가 선택지 주면 → 판단해서 선택하는 거예요.
- "프로젝트 만들까요?" 물어봐도 → 구조부터 정하고 만드는 게 순서예요.
실전 대화: 2단계 - 아키텍처 결정
이제 전체 구조를 정해요. 17편에서 배운 아키텍처 개념을 쓸 차례예요.
┌───────────────────────────────────────────────────────────────────────┐ │ 2단계: 아키텍처 결정 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ 나: "전체 구조는 레이어드 아키텍처로 갈 건데, │ │ 폴더를 레이어별로 나눠줘. │ │ │ │ 레이어 3개: │ │ 1. UI 레이어 (components/) - 화면에 보이는 것들 │ │ 2. 비즈니스 레이어 (services/) - 할 일 추가/삭제 로직 │ │ 3. 데이터 레이어 (lib/) - Supabase 연결, DB 접근 │ │ │ │ UI가 DB에 직접 접근하지 않고, │ │ 반드시 서비스 레이어를 거치게 해줘." │ │ │ │ AI: "레이어드 아키텍처로 폴더 구조 제안합니다: │ │ │ │ src/ │ │ ├── components/ (UI 레이어) │ │ │ ├── TodoList/ │ │ │ ├── TodoItem/ │ │ │ └── TodoForm/ │ │ ├── services/ (비즈니스 레이어) │ │ │ └── todoService.ts │ │ ├── lib/ (데이터 레이어) │ │ │ └── supabase.ts │ │ └── store/ (상태 관리) │ │ └── useTodoStore.ts │ │ │ │ 흐름: │ │ 컴포넌트 → 서비스 → Supabase 클라이언트 → DB │ │ │ │ 이렇게 하면 나중에 백엔드를 Supabase에서 다른 걸로 바꿔도, │ │ 서비스 레이어만 수정하면 돼요. 컴포넌트는 안 건드려도 됩니다." │ │ │ │ 나: "좋아. 그렇게 가자. │ │ 각 레이어가 뭘 하는지 주석으로 정리해서 보여줘." │ │ │ │ AI: "레이어 역할 정리: │ │ │ │ // UI 레이어 (components/) │ │ // - 사용자한테 보이는 화면 │ │ // - 버튼 누르면 서비스 레이어 호출 │ │ // - DB 접근 금지 │ │ │ │ // 비즈니스 레이어 (services/) │ │ // - 할 일 추가, 삭제, 완료 로직 │ │ // - 데이터 레이어 호출해서 DB 작업 │ │ // - UI는 이 레이어만 호출 │ │ │ │ // 데이터 레이어 (lib/) │ │ // - Supabase 클라이언트 설정 │ │ // - DB 테이블에 실제로 접근 │ │ // - 서비스 레이어만 호출 가능" │ │ │ └───────────────────────────────────────────────────────────────────────┘
이 대화에서 뭘 했나요?
- 17편(아키텍처)에서 배운 레이어드 아키텍처를 적용했어요.
- 폴더 구조로 레이어를 나눴어요.
- AI가 "왜 이렇게 하는지" 설명도 해줬어요.
실전 대화: 3단계 - 데이터 설계
이제 어떤 데이터를 어디에 저장할지 정해요. 16편(자료구조) + 19편(상태 관리)을 조합하는 거예요.
┌───────────────────────────────────────────────────────────────────────┐ │ 3단계: 데이터 설계 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ 나: "데이터 구조 정하자. │ │ │ │ 1. DB 테이블 (Supabase) │ │ users 테이블: │ │ - id, email, created_at (Supabase Auth가 만들어줌) │ │ │ │ todos 테이블: │ │ - id (UUID, 자동 생성) │ │ - user_id (users.id 외래키) │ │ - title (할 일 내용, text) │ │ - is_completed (완료 여부, boolean, 기본값 false) │ │ - created_at (생성일, timestamp) │ │ │ │ todos는 배열 구조로 관리하되, │ │ 각 항목은 객체 형태로." │ │ │ │ AI: "todos 테이블 스키마 확인: │ │ │ │ CREATE TABLE todos ( │ │ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), │ │ user_id UUID REFERENCES users(id) ON DELETE CASCADE, │ │ title TEXT NOT NULL, │ │ is_completed BOOLEAN DEFAULT false, │ │ created_at TIMESTAMP DEFAULT now() │ │ ); │ │ │ │ RLS (Row Level Security) 설정: │ │ - 자기 것만 보기/수정 가능하게 할까요?" │ │ │ │ na: "그래. RLS 켜줘. 본인 것만 접근 가능하게." │ │ │ │ AI: "RLS 정책 추가했습니다. │ │ │ │ 이제 상태 관리 설계가 필요해요. │ │ 어떤 데이터를 어디에 저장할까요?" │ │ │ │ na: "상태 관리는 이렇게: │ │ │ │ 1. 전역 상태 (zustand) │ │ - user: 로그인한 사용자 정보 (싱글톤 패턴) │ │ - todos: 할 일 목록 (배열) │ │ │ │ 2. DB (Supabase) │ │ - todos 테이블에 영구 저장 │ │ - 다른 기기에서도 접근 가능 │ │ │ │ 3. 로컬 상태 (컴포넌트 메모리) │ │ - TodoForm의 입력 중인 내용 │ │ - 입력 폼은 로컬 상태로, 추가 버튼 누를 때만 전역으로 │ │ │ │ 흐름: │ │ - 앱 시작 → DB에서 todos 가져와서 zustand에 넣기 │ │ - 할 일 추가 → zustand 업데이트 + DB에 저장 │ │ - 할 일 완료/삭제 → zustand 업데이트 + DB에 반영 │ │ │ │ 옵저버 패턴으로 zustand의 todos 바뀌면 │ │ TodoList, TodoCounter 자동 업데이트되게." │ │ │ │ AI: "상태 관리 설계 정리: │ │ │ │ 전역 상태 (zustand store): │ │ { │ │ user: { id, email } | null, │ │ todos: [ │ │ { id, title, is_completed, created_at } │ │ ], │ │ addTodo: (title) => { ... }, │ │ toggleTodo: (id) => { ... }, │ │ deleteTodo: (id) => { ... } │ │ } │ │ │ │ 로컬 상태 (TodoForm 컴포넌트): │ │ - inputValue (입력 중인 텍스트) │ │ │ │ DB 동기화: │ │ - 액션 실행 시 DB 저장 후 zustand 업데이트 │ │ - 옵티미스틱 업데이트 적용할까요? │ │ (UI 먼저 업데이트, DB는 백그라운드)" │ │ │ │ na: "옵티미스틱 업데이트는 나중에. 일단 DB 저장 성공하면 업데이트." │ │ │ └───────────────────────────────────────────────────────────────────────┘
이 대화에서 뭘 했나요?
- 16편(자료구조): todos는 배열로, 각 항목은 객체로 관리
- 19편(상태 관리): 전역 상태(zustand) + DB(Supabase) + 로컬 상태(입력 폼) 조합
- 18편(디자인 패턴): 로그인 상태는 싱글톤, todos 변화 감지는 옵저버 패턴
실전 대화: 4단계 - 컴포넌트 구조 설계
이제 UI를 어떻게 나눌지 정해요. 18편(디자인 패턴)에서 배운 Container-Presenter 패턴을 쓸게요.
┌───────────────────────────────────────────────────────────────────────┐ │ 4단계: 컴포넌트 구조 설계 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ na: "컴포넌트는 Container-Presenter 패턴으로 분리해줘. │ │ │ │ TodoList: │ │ - TodoListContainer: 데이터 가져오기, 상태 관리 │ │ - TodoListPresenter: 화면만 그리기 │ │ │ │ TodoForm: │ │ - TodoFormContainer: 입력 처리, 추가 로직 │ │ - TodoFormPresenter: 입력 폼 UI │ │ │ │ TodoItem: │ │ - TodoItemPresenter만 (로직 없음, props만 받음) │ │ │ │ Container는 로직, Presenter는 UI만. │ │ 나중에 디자인 바꾸려면 Presenter만 수정하면 되게." │ │ │ │ AI: "컴포넌트 구조 제안: │ │ │ │ components/TodoList/ │ │ ├── TodoListContainer.tsx (로직) │ │ │ - zustand에서 todos 가져오기 │ │ │ - toggleTodo, deleteTodo 함수 정의 │ │ │ - Presenter에 props 전달 │ │ │ │ │ └── TodoListPresenter.tsx (UI) │ │ - todos 배열 map 돌면서 TodoItem 렌더링 │ │ - props로 받은 핸들러 연결 │ │ │ │ components/TodoForm/ │ │ ├── TodoFormContainer.tsx (로직) │ │ │ - 로컬 상태로 input 관리 │ │ │ - addTodo 함수 호출 │ │ │ │ │ └── TodoFormPresenter.tsx (UI) │ │ - input, button 렌더링 │ │ - props로 받은 onChange, onSubmit 연결 │ │ │ │ components/TodoItem/ │ │ └── TodoItemPresenter.tsx (UI만) │ │ - 할 일 하나 표시 │ │ - 체크박스, 삭제 버튼 │ │ - 로직 없음, 모두 props │ │ │ │ 이렇게 하면: │ │ - UI 변경: Presenter만 수정 │ │ - 로직 변경: Container만 수정 │ │ - 테스트: Container/Presenter 따로 가능" │ │ │ │ na: "좋아. 페이지 구조도 정리해줘." │ │ │ │ AI: "페이지 구조: │ │ │ │ app/ │ │ ├── page.tsx (메인, 로그인 안 했으면 리다이렉트) │ │ ├── login/page.tsx (로그인 페이지) │ │ └── signup/page.tsx (회원가입 페이지) │ │ │ │ 메인 페이지(/)에서: │ │ - TodoFormContainer │ │ - TodoListContainer │ │ 렌더링." │ │ │ └───────────────────────────────────────────────────────────────────────┘
이 대화에서 뭘 했나요?
- 18편(디자인 패턴)에서 배운 Container-Presenter 패턴 적용
- 로직과 UI 분리 → 나중에 수정하기 쉽게
- 컴포넌트 폴더 구조까지 구체적으로 정함
실전 대화: 5단계 - 구현 및 확인
이제 드디어 만들고 확인하는 단계예요.
┌───────────────────────────────────────────────────────────────────────┐ │ 5단계: 구현 및 확인 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ na: "좋아, 이제 만들어줘. │ │ 근데 한 번에 다 만들지 말고, │ │ 기능 단위로 나눠서 하나씩 확인할게." │ │ │ │ AI: "어떤 순서로 만들까요? │ │ 제안: │ │ 1. Supabase 설정 + DB 테이블 │ │ 2. 로그인/회원가입 │ │ 3. 할 일 목록 보기 │ │ 4. 할 일 추가 │ │ 5. 할 일 완료/삭제" │ │ │ │ na: "그 순서대로 가자." │ │ │ └───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐ │ 5-1: Supabase 설정 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ AI: "Supabase 프로젝트 만들고, 환경변수 설정했어요. │ │ lib/supabase.ts에 클라이언트 생성했습니다. │ │ │ │ todos 테이블 생성 SQL: │ │ (SQL 코드...) │ │ │ │ Supabase 대시보드에서 이 SQL 실행해주세요." │ │ │ │ [직접 Supabase 대시보드 가서 SQL 실행] │ │ │ │ na: "테이블 만들었어. RLS도 켰고." │ │ │ └───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐ │ 5-2: 로그인/회원가입 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ AI: "로그인/회원가입 페이지 만들었어요. │ │ Supabase Auth 사용합니다. │ │ │ │ zustand store에 user 상태 추가했고, │ │ 로그인 성공하면 user에 정보 넣어요." │ │ │ │ [기능 단위로 직접 확인] │ │ 1. 회원가입 테스트 │ │ → 이메일, 비밀번호 입력 │ │ → 가입 성공 ✅ │ │ → Supabase Users 테이블에 추가됨 확인 │ │ │ │ 2. 로그인 테스트 │ │ → 방금 만든 계정으로 로그인 │ │ → 성공하면 메인(/) 페이지로 리다이렉트 ✅ │ │ → zustand store에 user 정보 들어감 확인 (개발자 도구) │ │ │ │ 3. 새로고침 테스트 │ │ → 어? 로그인 풀리네? │ │ │ │ na: "새로고침하면 로그인 풀리는데?" │ │ │ │ AI: "Supabase 세션 확인 로직 추가할게요. │ │ 앱 시작할 때 getSession() 호출해서 │ │ 세션 있으면 zustand에 user 넣겠습니다." │ │ │ │ [다시 확인] │ │ - 로그인 → 새로고침 → 로그인 유지됨 ✅ │ │ - 다른 탭에서 열기 → 로그인 상태 유지 ✅ │ │ │ └───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐ │ 5-3: 할 일 목록 보기 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ AI: "TodoListContainer, Presenter 만들었어요. │ │ │ │ 흐름: │ │ 1. 컴포넌트 마운트될 때 DB에서 todos 가져오기 │ │ 2. zustand store에 넣기 │ │ 3. Presenter가 todos 배열 map 돌면서 렌더링" │ │ │ │ [확인] │ │ - 로그인 후 메인 페이지 접속 │ │ - 할 일 목록 빈 상태로 표시됨 ✅ │ │ - Supabase 대시보드에서 직접 todo 추가 → 새로고침 → 보임 ✅ │ │ │ └───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐ │ 5-4: 할 일 추가 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ AI: "TodoFormContainer, Presenter 만들었어요. │ │ │ │ Container: │ │ - 로컬 상태로 input 관리 │ │ - 추가 버튼 누르면 todoService.addTodo() 호출 │ │ - 성공하면 zustand 업데이트 │ │ │ │ Presenter: │ │ - input, button UI │ │ - props로 받은 핸들러 연결" │ │ │ │ [확인] │ │ 1. 할 일 입력: "우유 사기" │ │ → 추가 버튼 클릭 │ │ → 목록에 바로 추가됨 ✅ │ │ → DB 확인: todos 테이블에 저장됨 ✅ │ │ │ │ 2. 여러 개 추가 │ │ → "빨래하기", "운동하기" 추가 │ │ → 모두 목록에 표시 ✅ │ │ │ │ 3. 새로고침 │ │ → 추가한 할 일들 모두 유지됨 ✅ │ │ │ └───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐ │ 5-5: 할 일 완료/삭제 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ AI: "완료, 삭제 기능 추가했어요. │ │ │ │ - 체크박스 클릭 → toggleTodo() │ │ - 삭제 버튼 클릭 → deleteTodo() │ │ - 둘 다 DB 업데이트 후 zustand 반영" │ │ │ │ [확인] │ │ 1. 완료 체크 │ │ → "우유 사기" 체크박스 클릭 │ │ → 취소선 표시됨 ✅ │ │ → DB의 is_completed = true 확인 ✅ │ │ │ │ 2. 삭제 │ │ → "빨래하기" 삭제 버튼 클릭 │ │ → 목록에서 사라짐 ✅ │ │ → DB에서도 삭제됨 확인 ✅ │ │ │ │ 3. 다른 기기 테스트 │ │ → 다른 브라우저(시크릿 모드)에서 로그인 │ │ → 같은 할 일 목록 보임 ✅ │ │ → 한쪽에서 완료 체크 → 다른쪽 새로고침 → 반영됨 ✅ │ │ │ └───────────────────────────────────────────────────────────────────────┘
이 단계에서 뭘 했나요?
- 기능 단위로 나눠서 하나씩 만들고 확인했어요.
- 문제 생기면 → 바로 피드백 → 수정 → 다시 확인
- "한 번에 다 만들어줘" 하지 않았어요. 이게 핵심이에요.
💡 왜 기능 단위 확인이 중요한가요?
AI가 코드 짜면 끝이 아니에요. 기능 단위로 직접 확인해야 해요.
- 버튼 만들었으면 → 눌러보고
- 저장 기능 만들었으면 → 저장해보고
- 삭제 기능 만들었으면 → 삭제해보고
이 "확인 → 피드백" 과정이 없으면 어떻게 될까요?
- 로그인은 되는데, 새로고침하면 풀리는 문제 → 5단계 후에 발견
- 할 일 추가는 되는데, DB 저장 안 됨 → 완성하고 나서 발견
- 다 만들고 보니 어디가 문제인지 모름 → 처음부터 다시
기능 단위로 확인하면:
- 문제가 생겼을 때 바로 발견 → 범위가 좁아서 고치기 쉬움
- "방금 만든 기능"이 문제라는 걸 바로 알 수 있음
- 완성도 높은 결과물 → 나중에 안 꼬임
전체 대화에서 사용한 PART 4 개념 정리
이 대화에서 PART 4 개념을 어떻게 썼는지 정리해볼게요.
16편: 자료구조
✅ todos를 배열로 관리
✅ 각 할 일은 객체 { id, title, is_completed }
✅ user_id로 users와 1:N 관계
17편: 아키텍처
✅ 레이어드 아키텍처 (UI / 서비스 / 데이터)
✅ 폴더 구조로 레이어 분리
✅ UI가 DB에 직접 접근 금지 → 서비스 레이어 경유
18편: 디자인 패턴
✅ Container-Presenter 패턴 (로직/UI 분리)
✅ 싱글톤 패턴 (로그인 상태 하나만)
✅ 옵저버 패턴 (zustand로 todos 변화 감지)
19편: 상태 관리
✅ 전역 상태 (zustand): user, todos
✅ DB (Supabase): 영구 저장
✅ 로컬 상태 (메모리): 입력 폼
✅ 조합: 전역(앱 공유) + DB(영구 저장) AI한테 이렇게 요청하세요
상황 1: 프로젝트 시작할 때
❌ 나쁜 예 (한 번에 다 말하기)
na: "Next.js로 할 일 관리 앱 만들어줘.
로그인, 회원가입, 할 일 추가/삭제/완료 기능 넣고,
Supabase 써서 DB 저장하고,
다른 기기에서도 동기화되게 해줘."
왜 이렇게 하면 안 될까요?
AI가 알아서 만들어주긴 해요. 근데 구조를 어떻게 잡았는지 모르죠.
- 폴더를 어떻게 나눴는지 → 모름
- 데이터를 어디에 저장했는지 → 모름
- 컴포넌트를 어떻게 분리했는지 → 모름
나중에 기능 추가하려면? → "이 코드가 뭘 하는 건지 모르겠는데..." → 다시 만들기 반복
✅ 좋은 예 (단계별로 대화)
1단계: 기술 스택
na: "할 일 관리 웹앱 만들 건데,
Next.js, Supabase, zustand 쓸 거야."
2단계: 아키텍처
na: "레이어드 아키텍처로 폴더 나눠줘.
UI / 서비스 / 데이터 레이어로."
3단계: 데이터 설계
na: "todos 테이블은 이 필드들로 만들어줘: ..."
na: "상태는 zustand(전역) + DB(영구) 조합으로."
4단계: 컴포넌트 구조
na: "Container-Presenter 패턴으로 분리해줘."
5단계: 구현
na: "기능 단위로 하나씩 만들자. 로그인부터."
상황 2: AI가 제안하면 판단하기
✅ AI의 제안을 듣고 → 판단 → 선택
AI: "상태 관리 옵션:
(앱 전체에서 데이터 공유하는 방법이에요)
1. React Context - 기본 탑재, 근데 복잡해질 수 있음
2. zustand - 코드 짧고 배우기 쉬움 ⭐ 추천
3. Redux - 대기업용, 배울 게 많음
할 일 앱 규모면 zustand 추천드려요."
나: "zustand로 가자.
네가 추천하는 이유가 '코드 짧고 배우기 쉬움'이라고 했잖아.
우리 프로젝트 작으니까 그게 맞겠다."
AI한테 전부 맡기지 말고, 선택은 내가 하는 거예요.
AI가 각 옵션을 설명해주면, 왜 그게 좋은지 이해하고 선택하는 거예요. "알아서 잘 해줘"는 안 통해요. 방향은 여러분이 잡아야 해요.
상황 3: 구조 먼저, 구현은 나중에
❌ 나쁜 예 (바로 구현)
na: "할 일 관리 앱 만들어줘."
AI: "프로젝트 생성합니다..."
→ 구조도 안 정했는데 만들기 시작
✅ 좋은 예 (구조 먼저)
na: "Next.js로 할 일 관리 앱 만들 건데,
아키텍처부터 정하자."
AI: "프로젝트 생성할까요?"
na: "아직 안 돼. 구조 먼저 정하고 만들자."
코드 문법 몰라도 돼요
💡 잠깐, Container-Presenter 패턴 코드 어떻게 짜는지 몰라요.
괜찮아요. 개념만 알면 돼요.
여러분이 알아야 할 것:
✅ Container는 로직, Presenter는 UI
✅ 레이어드 아키텍처는 폴더로 레이어 나누는 거
✅ zustand는 전역 상태 관리 도구
✅ todos는 배열로 관리하는 게 적합
여러분이 몰라도 되는 것:
❌ Container 코드 문법
❌ zustand store 만드는 법
❌ Supabase 클라이언트 설정 코드
→ AI한테 "Container-Presenter 패턴으로 분리해줘" 하면 됨.
→ "zustand로 전역 상태 관리해줘" 하면 됨.
근데 하면 할수록 코드가 눈에 들어와요.
"아, Container에서 이 함수를 Presenter로 넘겨주는구나" "zustand store에서 todos를 가져오는구나"
이게 보이기 시작하면 고도화가 훨씬 쉬워져요.
실전 팁
1. 한 번에 다 말하지 말기
✅ "로그인부터 만들자. 다음은 할 일 추가."
2. 구조 정하고 만들기
✅ "구조부터 정하자" → 폴더/레이어/패턴 합의 → 그다음 코드
3. 기능 단위로 확인
✅ 로그인 만들고 → 테스트 → 할 일 추가 만들고 → 테스트
4. AI 제안 듣고 판단하기
✅ AI가 옵션 주면 → "1번으로 가자, 왜냐면..."
5. 문제 생기면 바로 피드백
나: "어? 새로고침하면 로그인 풀리는데?"
AI: "세션 확인 로직 추가할게요."
→ 문제를 구체적으로 말하면 AI가 해결책 제시
오늘의 핵심 정리
→ 자료구조 + 아키텍처 + 디자인 패턴 + 상태 관리
✅ 한 번에 다 말하는 게 아니라 단계적으로
→ 기술 스택 → 아키텍처 → 데이터 → 컴포넌트 → 구현
✅ 구조를 먼저 정하고 만든다
→ "만들어줘" 하기 전에 폴더/레이어/패턴 합의
✅ 기능 단위로 확인한다
→ 로그인 만들고 → 확인 → 할 일 추가 만들고 → 확인
✅ AI한테 요청할 때:
"할 일 관리 앱 만들어줘" (X)
"Next.js + Supabase로 할 일 관리 앱 만들 건데,
레이어드 아키텍처로 폴더 나누고,
Container-Presenter 패턴으로 컴포넌트 분리해줘.
상태는 zustand(전역) + Supabase(DB) 조합.
기능 단위로 하나씩 만들자." (O)
✅ "구조 잡아줘" → "이 패턴으로 해줘"
→ 이 수준으로 말할 수 있으면 고도화 가능셀프체크
이 글을 이해했다면 아래 질문에 답할 수 있어요:
- 프로젝트 시작할 때 AI랑 대화 순서는? (기술 스택 → ? → ? → ?)
- "만들어줘" 하기 전에 뭘 먼저 정해야 하나요?
- PART 4 개념 중 어떤 걸 어디에 적용했나요?
- 기능 단위 확인이 왜 중요한가요?
정답 보기
- 대화 순서: 기술 스택 → 아키텍처 → 데이터 설계 → 컴포넌트 구조 → 구현
- 먼저 정할 것: 아키텍처(폴더 구조), 데이터 설계, 컴포넌트 패턴
- PART 4 적용:
- 16편(자료구조): todos 배열, user_id 관계
- 17편(아키텍처): 레이어드 (UI/서비스/데이터)
- 18편(디자인 패턴): Container-Presenter, 싱글톤, 옵저버
- 19편(상태 관리): zustand(전역) + DB(영구) + 로컬(입력)
- 기능 단위 확인: 문제 생기면 바로 발견 → 고치기 쉬움 → 나중에 안 꼬임
다음 글 예고
오늘은 AI한테 설계 지시하는 법을 배웠어요. PART 4에서 배운 모든 개념(자료구조, 아키텍처, 디자인 패턴, 상태 관리)을 조합해서 실전 대화를 해봤어요.
PART 4가 끝났어요. 이제 기술 개념을 알았고, AI한테 어떻게 지시하는지도 알아요.
다음 글부터는 PART 5: 첫 프로젝트 - 웹 서비스가 시작돼요.
21편: 기획과 아키텍처
- 실제 프로젝트를 처음부터 끝까지 만들기
- 할 일 관리 앱을 배포까지
- "React + Supabase로 갈 건데, 아키텍처 추천해봐"
개념을 배웠으면, 이제 직접 만들면서 손에 붙여야 해요. 21편부터 함께 프로젝트를 만들어봐요.
이 시리즈 로드맵
PART 4: 기술 개념 - 설계
[ 16편 ] 자료구조 개념 ✅
↓
[ 17편 ] 아키텍처 개념 ✅
↓
[ 18편 ] 디자인 패턴 개념 ✅
↓
[ 19편 ] 상태 관리 개념 ✅
↓
[ 20편 ] AI한테 설계 지시하는 법 (지금 여기!)
↓
PART 5: 첫 프로젝트 - 웹 서비스
[ 21편 ] 기획과 아키텍처
↓
...
궁금한 점이나 다뤘으면 하는 주제가 있으면 댓글로 남겨주세요!