-
클린 아키텍쳐를 프로젝트에 적용해보자프론트엔드 2025. 8. 5. 23:47
클린 아키텍쳐
- 클린 아키텍쳐는 소프트웨어 시스템을 계층적으로 분리하는 설계 방식이다.
클린 아키텍쳐 관련 글은 작성한 것이 있으니 참고하면 좋을거 같다
간단하게 정리하면
- 클린 아키텍쳐는 내부 계층이 외부 계층을 의존하지 않아야 한다는 특징이 있다.
도메인 계층은 Entity와 Repository가 있다.
Entity : 핵심 비지니스 객체이다. -> DB에서 User 데이터가 있다면 User 데이터 객체를 의미한다.
Repository : 저장 조회같은 도메인 관점의 데이터 처리를 명세한다 -> 말 그대로 데이터 처리 명세에 초점을 둔다import { FeedBackEntity } from "@/backend/feedbacks/domains/entities/FeedBackEntity";
export interface FeedBackRepository {//createAddFeedBack(feedBack: FeedBackEntity): Promise<FeedBackEntity>;}-> 위 코드에서 보면 AddFeedBack 데이터를 처리하기 위해 명세를 한 것을 볼 수 있다.
어플리케이션 계층은 DTO, UseCase가 있다.
DTO: API 요청시 필요한 데이터 객체를 정의한다
UseCase : 기능 단위로 로직을 실행한다 -> 어떻게 동작할지만 정의하고 구현은 모른다!import { PrFeedBackRepository } from "@/backend/feedbacks/infrastructures/repositories/PrFeedBackRepository";import { FeedBackEntity } from "@/backend/feedbacks/domains/entities/FeedBackEntity";
export class GetRoutineReviewListUsecase {constructor(public readonly PrFeedBackRepository: PrFeedBackRepository) {}
async execute(feedBack: FeedBackEntity) {return this.PrFeedBackRepository.AddFeedBack(feedBack);}}-> UseCase 코드를 보면 execute 안에 실제 실행되어야 할 함수를 넣었다.
인프라계층은 Repository가 있다
Repository : 인프라계층은 외부 라이브러리 등에 의존을 하기 때문에 레포지토리안에 외부 라이브러리 코드를 사용한다
import { FeedBackEntity } from "@/backend/feedbacks/domains/entities/FeedBackEntity";import { FeedBackRepository } from "@/backend/feedbacks/domains/repositories/FeedBackRepository";import prisma from "@/public/utils/prismaClient";
export class PrFeedBackRepository implements FeedBackRepository {async AddFeedBack(feedBack: FeedBackEntity): Promise<FeedBackEntity> {const createdFeedBack = await prisma.feedback.create({data: {gptResponseContent: feedBack.gptResponseContent,challengeId: feedBack.challengeId,},});
return new FeedBackEntity(createdFeedBack.gptResponseContent,createdFeedBack.challengeId);}}-> 코드를 보면 prisma를 create를 하는 모습을 볼 수 있다.
어댑터 계층은 Next.js에서 route.ts가 있다
Adapter : 외부와 useCase를 이어준다.
import { GetRoutineReviewListUsecase } from "@/backend/feedbacks/applications/usecases/GetRoutineReviewListUsecase";import { PrFeedBackRepository } from "@/backend/feedbacks/infrastructures/repositories/PrFeedBackRepository";import { NextRequest, NextResponse } from "next/server";
export const POST = async (request: NextRequest) => {const body = await request.json();
try {const feedbackRepo = new PrFeedBackRepository();const feedBackUseCase = new GetRoutineReviewListUsecase(feedbackRepo);const result = await feedBackUseCase.execute(body);
if (!result) {return NextResponse.json({ error: "피드백 데이터 저장에 실패했습니다." },{ status: 400 });}return NextResponse.json({ message: "피드백 데이터가 저장되었습니다." },{ status: 200 });} catch (error) {console.error(error);return NextResponse.json({ error: "Internal Server Error" },{ status: 500 });}};-> 위 코드에서 보면 route.ts로 POST를 보내는데 useCase를 연결 해주는 것을 볼 수 있다.
결론 🤔
- 프로젝트 초반엔 구조가 복잡하게 느껴졌지만,
- 기능별 책임이 명확하게 나뉘니까 협업과 유지보수가 훨씬 쉬워짐
- 실제로 하나의 기능을 고칠 때 다른 부분에 영향이 거의 없어서,
- 버그 발생률도 줄고 테스트 코드도 더 잘 작성할 수 있었다.
'프론트엔드' 카테고리의 다른 글
Date.prototype.toISOString() (0) 2025.08.13 Next.js 미들웨어로 온보딩 접근 제어 구현하기 (2) 2025.08.13 TanStack Query 적용해보기 (5) 2025.08.12 Git Merge 방법 (3) 2025.08.02 클린아키텍쳐 (0) 2025.07.31