PromleeBlog
sitemap
aboutMe

posting thumbnail
Vibe Coding 워크플로우 풀스택 앱 빠르게 만들기 (Wasp 활용)
A Structured Workflow for Vibe Coding Full-Stack Apps (with Wasp)

📅

🚀

들어가기 전에 🔗

오늘은 조금 특별한 개발 방식인 "Vibe Coding"과, 이를 더욱 체계적으로 현실화할 수 있도록 도와주는 풀스택 프레임워크
Wasp
를 활용한 개발 워크플로우에 대해 이야기해 보려고 합니다.
복잡한 계획에 너무 많은 시간을 쏟기보다, 좋은 "느낌(Vibe)"을 따라 빠르게 아이디어를 구체화하고 실제 동작하는 애플리케이션을 만들어보는 이 매력적인 접근법을 함께 살펴보시죠.

🚀

Vibe Coding이란? 🔗

"Vibe Coding"은 완벽하고 상세한 계획을 세우기보다는, 떠오르는 아이디어나 좋은 느낌(Vibe)에 집중하여 빠르게 프로토타입을 만들고 점진적으로 발전시켜 나가는 개발 스타일을 의미합니다.
특히 풀스택 애플리케이션 개발 초기에는 너무 많은 고민과 설정으로 인해 시작조차 못 하는 경우가 많은데, Vibe Coding은 "일단 만들어보자!(Just Ship It!)" 정신으로 이러한 장벽을 낮춰줍니다.

물론, 무작정 코드만 작성하는 것이 아니라, 핵심 아이디어를 빠르게 검증하고 사용자 피드백을 통해 방향을 잡아나가는 것이 중요합니다. 이때 Wasp와 같은 도구가 큰 도움을 줄 수 있습니다.
바이브 코딩
바이브 코딩

🚀

Wasp와 함께하는 체계적인 Vibe Coding 워크플로우 🔗

Wasp는 React, Node.js, Prisma를 기반으로 하는 풀스택 웹 애플리케이션 개발 프레임워크로, 간단한 설정 파일(.wasp 파일)을 통해 많은 보일러플레이트 코드를 자동으로 생성해주어 개발자가 핵심 로직에만 집중할 수 있도록 도와줍니다.
Wasp를 활용한 구조화된 워크플로우는 다음과 같습니다.

1단계: 아이디어 스케치 - 종이와 펜으로 시작해요 🔗

모든 위대한 앱도 처음에는 간단한 스케치에서 시작합니다!
가장 먼저, 만들고 싶은 애플리케이션의 핵심 기능과 대략적인 화면 흐름을 종이와 펜(또는 간단한 다이어그램 도구)을 이용해 빠르게 그려봅니다.
너무 자세할 필요는 없습니다. 중요한 것은 어떤 데이터를 다루고, 사용자가 어떤 상호작용을 할 것인지에 대한 큰 그림을 잡는 것입니다.


➡️

예시 (할 일 관리 앱 스케치) 🔗

할 일 관리 앱 스케치
할 일 관리 앱 스케치

2단계: Wasp 프로젝트 설정과 뼈대 만들기 🔗

아이디어가 어느 정도 정리되었다면, Wasp CLI를 사용하여 새 프로젝트를 생성합니다.
  # Wasp 설치 (아직 안 했다면)
curl -sSL https://get.wasp-lang.dev/installer.sh | sh 
  # 새 Wasp 프로젝트 생성
wasp new MyAwesomeApp
cd MyAwesomeApp

그다음, 프로젝트의 루트 디렉터리에 있는 .wasp 파일(예: main.wasp)을 열어 앱의 기본적인 정보와 데이터 모델을 정의합니다.
Wasp는 이 파일을 기반으로 필요한 코드의 상당 부분을 자동으로 생성해줍니다.
➡️

예시 (main.wasp 파일) 🔗

// main.wasp
app MyAwesomeApp {
  wasp: {
    version: "^0.13.0" // Wasp 버전 명시
  },
  title: "My Awesome To-Do App", // 앱 제목
  auth: { // 사용자 인증 기능 설정
    userEntity: User, // 사용자 정보를 담을 엔티티 (아래 정의)
    methods: {
      usernameAndPassword: {} // 아이디/비밀번호 방식 사용
    },
    onAuthFailedRedirectTo: "/login"
  },
  // 이메일 발송 기능 사용 여부 (여기서는 예시로 false)
  emailSender: {
    type: "dummy", // 개발 중에는 실제 이메일 대신 콘솔에 출력
    fromAddress: "noreply@example.com"
  }
}
 
// 사용자 데이터 모델 정의
entity User {
  username: String @unique,
  password: String,
  tasks: [Task] @relation(name: "UserTasks") // 사용자와 할 일(Task) 관계 정의
}
 
// 할 일(Task) 데이터 모델 정의 (Prisma 스키마와 유사)
entity Task {
  description: String,
  isDone: Boolean @default(false), // 기본값은 false
  user: User @relation(name: "UserTasks", fields: [userId], references: [id]), // Task는 User에 속함
  userId: Int
}
 
// 라우트 정의 (웹 페이지 경로)
route RootRoute { path: "/", to: MainPage } // 루트 경로는 MainPage 컴포넌트로 연결
page MainPage { // MainPage 정의
  component: import { MainPage } from "@src/MainPage.tsx", // 실제 React 컴포넌트 파일 경로
  authRequired: true // 이 페이지는 로그인 필요
}
 
route LoginRoute { path: "/login", to: LoginPage }
page LoginPage {
  component: import { LoginPage } from "@src/LoginPage.tsx"
}
// ... 기타 페이지 및 라우트 정의
.wasp 파일을 저장하면, Wasp가 자동으로 필요한 파일 구조와 기본 코드를 src/ 디렉터리 등에 생성하거나 업데이트합니다.

3단계: 핵심 백엔드 기능 구현하기 (Wasp Operations) 🔗

데이터 모델과 페이지가 정의되었다면, 이제 실제 서버 로직을 구현할 차례입니다.
Wasp에서는 이를
Operations
라고 부르며, 크게 데이터를 읽어오는
Query
와 데이터를 생성/수정/삭제하는
Action
으로 나뉩니다.
이들은 src/actions.tssrc/queries.ts (또는 사용자가 정의한 다른 파일)에 TypeScript 함수 형태로 작성합니다.
➡️

예시: 할 일 생성 Action (src/actions.ts) 🔗

src/actions.ts
import { HttpError } from '@wasp/core/HttpError.js'; // Wasp가 제공하는 HttpError 클래스
import { type Task } from '@wasp/entities'; // .wasp 파일에서 정의한 Task 엔티티 타입
import { type CreateTask } from '@wasp/actions/types'; // Wasp가 생성해주는 Action 타입
 
// CreateTask 타입은 입력(args)과 반환값(Task)의 타입을 지정합니다.
export const createTask: CreateTask<{ description: string }, Task> = async (args, context) => {
  // context.user를 통해 현재 로그인한 사용자 정보에 접근 가능 (auth 설정 시)
  if (!context.user) {
    throw new HttpError(401); // 로그인하지 않은 사용자는 에러 발생
  }
 
  // context.entities를 통해 Prisma Client처럼 데이터베이스와 상호작용 가능
  const newTask = await context.entities.Task.create({
    data: {
      description: args.description,
      user: { connect: { id: context.user.id } } // 현재 사용자와 Task 연결
    }
  });
  return newTask;
};
➡️

예시: 모든 할 일 가져오는 Query (src/queries.ts) 🔗

src/queries.ts
import { HttpError } from '@wasp/core/HttpError.js';
import { type Task } from '@wasp/entities';
import { type GetTasks } from '@wasp/queries/types'; // Wasp가 생성해주는 Query 타입
 
// GetTasks 타입은 입력(args)이 없고, Task 배열을 반환함을 지정합니다.
export const getTasks: GetTasks<void, Task[]> = async (args, context) => {
  if (!context.user) {
    throw new HttpError(401);
  }
  return context.entities.Task.findMany({
    where: { user: { id: context.user.id } }, // 현재 사용자의 할 일만 가져오기
    orderBy: { id: 'desc' } // 최신순으로 정렬
  });
};
Wasp는 이렇게 정의된 Action과 Query를 프론트엔드에서 쉽게 호출할 수 있는 타입-세이프한 훅(예: useAction, useQuery)을 자동으로 생성해줍니다.

4단계: 프론트엔드 만들기 (React + Tailwind CSS) 🔗

이제 .wasp 파일에서 정의한 페이지 컴포넌트(예: src/MainPage.tsx)를 실제로 구현합니다.
Wasp는 기본적으로 React를 사용하며, Tailwind CSS도 쉽게 통합하여 사용할 수 있습니다.
➡️

예시 (src/MainPage.tsx): 🔗

src/MainPage.tsx
import React, { useState } from 'react';
import { useQuery } from '@wasp/queries';         // Wasp가 생성한 useQuery 훅
import { useAction } from '@wasp/actions';       // Wasp가 생성한 useAction 훅
import getTasks from '@wasp/queries/getTasks';   // 위에서 정의한 Query 임포트
import createTask from '@wasp/actions/createTask'; // 위에서 정의한 Action 임포트
import logout from '@wasp/auth/logout.js';      // Wasp가 제공하는 로그아웃 Action
 
export function MainPage() {
  // getTasks Query 호출 (데이터, 로딩 상태, 에러 객체 반환)
  const { data: tasks, isLoading, error: tasksError } = useQuery(getTasks);
  // createTask Action 사용 준비
  const createTaskFn = useAction(createTask);
  // 새 할 일 입력을 위한 상태
  const [newTaskDescription, setNewTaskDescription] = useState('');
 
  const handleCreateTask = async () => {
    if (!newTaskDescription) return;
    try {
      await createTaskFn({ description: newTaskDescription }); // Action 실행
      setNewTaskDescription(''); // 입력창 비우기
    } catch (err: any) {
      window.alert('Error: ' + (err.message || 'Failed to create task'));
    }
  };
 
  if (isLoading) return <div>Loading...</div>;
  if (tasksError) return <div>Error: {tasksError.message}</div>;
 
  return (
    <div className="p-4">
      <button 
        onClick={logout} 
        className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded float-right"
      >
        Logout
      </button>
      <h1 className="text-2xl font-bold mb-4">My To-Do List</h1>
      <div className="flex mb-4">
        <input
          type="text"
          className="border rounded-l px-4 py-2 w-full"
          placeholder="Enter new task..."
          value={newTaskDescription}
          onChange={(e) => setNewTaskDescription(e.target.value)}
        />
        <button
          onClick={handleCreateTask}
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r"
        >
          Add Task
        </button>
      </div>
      <ul>
        {tasks?.map((task) => (
          <li key={task.id} className={`p-2 mb-2 border rounded ${task.isDone ? 'line-through bg-green-100' : 'bg-white'}`}>
            {task.description}
            {/* 여기에 완료/미완료 처리 버튼 등을 추가할 수 있습니다. */}
          </li>
        ))}
      </ul>
    </div>
  );
}

5단계: 간편 배포 🔗

Wasp는 wasp deploy 라는 CLI 명령어를 통해 Fly.io와 같은 플랫폼에 매우 간편하게 배포할 수 있는 기능을 제공합니다.
복잡한 배포 설정을 직접 하지 않아도 되어 프로토타입을 빠르게 출시하고 피드백을 받는 데 유리합니다.

6단계: 계속해서 발전시키기 🔗

첫 번째 버전을 배포했다고 끝이 아닙니다. Vibe Coding의 핵심은
빠른 반복
입니다.
사용자들의 피드백을 듣거나, 문득 떠오르는 좋은 아이디어("Vibe")가 있다면, 다시 .wasp 파일을 수정하고(예: 새로운 엔티티나 페이지 추가), 백엔드 Operation을 만들고, 프론트엔드 UI를 개선하는 과정을 반복합니다.

🚀

Vibe Coding을 위한 추가 팁 🔗

wasp를 이용한 vibe 코딩하며
wasp를 이용한 vibe 코딩하며

🚀

결론 🔗

오늘은 "Vibe Coding"이라는 개발 스타일과 이를 Wasp 프레임워크를 통해 체계적으로 수행하는 워크플로우에 대해 알아보았습니다.
아이디어를 빠르게 스케치하고, .wasp 파일로 핵심을 정의한 뒤, 백엔드와 프론트엔드를 순차적으로(또는 동시에) 개발하고, 간편하게 배포하며, 지속적으로 개선해 나가는 과정은 매우 매력적입니다.

Wasp와 같은 도구는 풀스택 개발의 복잡성을 줄여주어 개발자가 아이디어 구현 자체에 더 집중할 수 있도록 돕습니다.
여러분도 자신만의 "Vibe"를 따라 멋진 애플리케이션을 빠르게 만들어보는 경험을 해보시길 바랍니다.

참고 🔗