Project CRUD — Build 네임스페이스 컨트롤러 + 뷰 + 소유자 접근제어 + 테스트
ID: 3519573c-4d56-4fc5-b583-91ecb7843409
## 목표
Build 네임스페이스에서 Project CRUD 완전 구현. raw_idea 입력, status 필터, 소유자 접근 제어.
## 현재 상태
- Project 모델 존재: belongs_to :user, has_many :build_steps, has_many :ai_conversations, has_many :showcase_services. enum :status { ideating: 0, building: 1, launched: 2, abandoned: 3 }. validates :title. completion_percentage 메서드.
- Build::ProjectsController 스텁 존재 (빈 액션)
- Build::HomeController 스텁 존재
- 라우트: `namespace :build { get "/", to: "home#index"; resources :projects do ... end }`
- 공용 Partial: _button, _card, _input, _flash, _navbar
## 구현 사항
### 1. Build::ProjectsController 완전 구현
```ruby
class Build::ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
before_action :authorize_owner!, only: [:show, :edit, :update, :destroy]
def index
@projects = Current.user.projects.order(updated_at: :desc)
# status 필터: ?status=ideating / building / launched / abandoned
@projects = @projects.where(status: params[:status]) if params[:status].present?
end
def show; end
def new
@project = Current.user.projects.build
end
def create
@project = Current.user.projects.build(project_params)
if @project.save
redirect_to build_project_path(@project), notice: "프로젝트가 생성되었습니다."
else
render :new, status: :unprocessable_entity
end
end
def edit; end
def update
if @project.update(project_params)
redirect_to build_project_path(@project), notice: "프로젝트가 수정되었습니다."
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@project.destroy
redirect_to build_projects_path, notice: "프로젝트가 삭제되었습니다."
end
private
def set_project
@project = Project.find(params[:id])
end
def authorize_owner!
redirect_to build_projects_path, alert: I18n.t("errors.messages.not_authorized") unless @project.user == Current.user
end
def project_params
params.require(:project).permit(:title, :description, :raw_idea, :status)
end
end
```
### 2. Build::HomeController
```ruby
class Build::HomeController < ApplicationController
def index
@projects = Current.user.projects.order(updated_at: :desc).limit(10)
end
end
```
### 3. 뷰 (app/views/build/projects/)
- **다크 테마** (앱 내부 — bg-bg, text-text-primary, bg-surface, bg-accent)
- 기존 Partial 활용 (_button, _card, _input)
- index: 프로젝트 카드 그리드, status 필터 탭 (전체/기획중/개발중/출시/포기), 새 프로젝트 버튼
- show: 프로젝트 상세 (title, description, raw_idea, status, completion_percentage). build_steps 목록 (있으면). 편집/삭제 버튼
- new/edit: 폼 (_form partial)
- title (필수), description (textarea), raw_idea (textarea — "아이디어를 자유롭게 적어주세요")
- status는 create 시 자동 ideating, edit 시만 변경 가능
- _form: title, description, raw_idea 입력. status 선택 (edit 시만)
### 4. 소유자 접근 제어
- show/edit/update/destroy: 소유자(Current.user == project.user)만 접근
- 다른 사용자 접근 시 리다이렉트 + alert
### 5. Build 홈 뷰 (app/views/build/home/index.html.erb)
- 최근 프로젝트 카드 (없으면 "첫 프로젝트를 시작해보세요" CTA)
- 새 프로젝트 만들기 버튼
### ⚠️ 주의
- developer-1이 동시에 Admin::CurriculaController 작업 중 — build/ 범위만 수정
- Project 모델(project.rb) 수정 불필요 (이미 완성됨)
- User 모델 건드리지 않기
- 한국어 Flash 메시지
- status 필터 쿼리에서 SQL injection 주의 (enum 값 검증)
### 테스트 (TDD)
- Build::ProjectsController 테스트:
- 인증 필요 (미인증 → 리다이렉트)
- CRUD 전체 동작
- 소유자만 접근 가능 (타인 프로젝트 → 리다이렉트)
- status 필터 동작
- raw_idea 입력 저장
- 유효하지 않은 입력 시 에러
- Build::HomeController 테스트:
- 프로젝트 목록 표시
- fixture: projects.yml에 이미 데이터 있을 것 — user 연결 확인
### 완료 기준
- Build 프로젝트 CRUD 전체 동작
- raw_idea 입력 + 저장
- status 필터 동작
- 소유자 외 접근 시 리다이렉트
- 컨트롤러 테스트 통과
- bin/rails test 전체 통과
첨부 이미지
이미지 추가 (Ctrl+V로 붙여넣기 또는 클릭)
JPEG, PNG, GIF, WebP / 최대 10MB
활동 로그
-
Ddeveloper-2 상태 변경: 할 일 → 리뷰
2026년 03월 26일 08:25:53