[2024.06.11 TIL] 내일배움캠프 39일차 (Node.js 심화 강의, Layered Architecture Pattern)

My_Code·2024년 6월 11일
0

TIL

목록 보기
51/113
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.


💻 TIL(Today I Learned)

📌 Today I Done

✏️ 아키텍처 패턴 (Architecture Pattern)

  • 아키텍처 패턴은 소프트웨어의 구조를 구성하기 위한 가장 기본적인 토대를 제시함

  • 각각의 시스템들과 그 역할이 정의되어어 있고, 여러 시스템 사이의 관계와 규칙 등이 포함되어 있음

  • 복잡한 문제를 해결할 때, 아키텍처 패턴을 이용하면 모델이나 코드를 더 쉽게 변경할 수 있음

  • MVC 패턴(Model View Controller Pattern)
    • 하나의 애플리케이션을 Model, View, Controller로 이루어진 3개의 측면으로 분리하여 개발하는 디자인 패턴
    • 모델(Model) : 어떻게 데이터가 변경되고 조작될 수 있는지에 관한 규칙 정의를 담당
    • 뷰(View) : 프론트엔드와 같은 사용자 인터페이스를 담당
    • 컨트롤러(Controller) : Model을 통해 받은 데이터를 처리하거나, 결과 값을 View에 반환하는 역할을 담당

  • 클린 아키텍처 패턴(Clean Architecture)
    • 소프트웨어를 내부 도메인으로 향하는 의존성을 가지는 여러 계층으로 분리하는 패턴
    • 즉, 추상화 개념(Abstraction principle)으로써 관심사를 분리시키고 의존도를 낮추는 것에 목적을 둔 아키텍처
    • 안쪽에 위치할수록 고수준 정책이며, 바깥쪽에 위치할 수록 저수준 정책을 의미

  • 마이크로 서비스 아키텍처 패턴(MicroServices Architecture Pattern, MSA)
    • 시스템을 작고, 독립적으로 배포 가능한 서비스로 분할하는 패턴
    • 하나의 큰 어플리케이션을 여러개의 작은 어플리케이션으로 쪼개어 변경과 조합이 가능하도록 만든 아키텍쳐
    • 하나의 시스템에서 다양한 언어와 프레임워크를 도입할 수 있는 패턴

✏️ 계층형 아키텍처 패턴 (Layered Architecture Pattern)

  • 시스템을 여러 계층으로 분리하여 관리하는 아키텍처 패턴

  • 각 계층이 자신의 바로 아래 계층에만 의존하게 만드는 것이 목표

  • 상위 계층은 하위 계층을 사용할 수 있지만, 하위 계층은 자신의 상위 계층을 알지 못함

  • 3계층 아키텍처의 계층

    • 프레젠테이션 계층 (Presentation Layer)
    • 비즈니스 로직 계층 (Business Logic Layer)
    • 데이터 엑세스 계층 (Data Access Layer) | 영속 계층(Persistence Layer)

  • 계층형 아키텍처 패턴의 장점

    • 계층별로 분리했기 때문에 각 기능에 대한 코드를 명확하게 인지 가능
    • 각 계층은 독립적이며, 의존성이 낮기 때문에 코드 수정이 용이
    • 계층별로 단위 테스트 코드를 작성할 수 있어 테스트에 용이

✏️ Express로 구현하는 3계층 아키텍처

내 프로젝트 폴더 이름
├── package.json
├── prisma
│   └── schema.prisma
├── src
│   ├── app.js
│   ├── controllers
│   │   └── posts.controller.js
│   ├── middlewares
│   │   ├── error-handling.middleware.js
│   │   └── log.middleware.js
│   ├── repositories
│   │   └── posts.repository.js
│   ├── routes
│   │   ├── index.js
│   │   └── posts.router.js
│   ├── services
│   │   └── posts.service.js
│   └── utils
│       └── prisma
│           └── index.js
└── yarn.lock

✏️ 컨트롤러 (Controller)

  • 프레젠테이션 계층(Presentation Layer)은 가장 먼저 클라이언트의 요청을 만나는 계층

  • 컨트롤러는 클라이언트의 요청을 처리하고, 서버에서 처리된 결과를 반환함

  • 1) 클라이언트의 요청(Request)을 수신

  • 2) 요청(Request)에 들어온 데이터 및 내용을 검증 후 서비스로 전달

  • 3) 서버에서 수행된 결과를 클라이언트에게 반환(Response)

// src/routes/posts.router.js

import express from 'express';
import { PostsController } from '../controllers/posts.controller.js';

const router = express.Router();

// PostsController의 인스턴스를 생성
const postsController = new PostsController();

/** 게시글 조회 API **/
router.get('/', postsController.getPosts);
/** 게시글 상세 조회 API **/
router.get('/:postId', postsController.getPostDetail);
/** 게시글 작성 API **/
router.post('/', postsController.createPost);
/** 게시글 수정 API **/
router.patch('/:postId', postsController.updatePost);
/** 게시글 삭제 API **/
router.delete('/:postId', postsController.deletePost);

export default router;
// src/controllers/posts.controller.js

import { PostsService } from '../services/posts.service.js';

// 사용자의 입력을 받아서 Service로 넘기는 역할의 클래스
export class PostsController {
  // Post 서비스 클래스를 컨트롤러 클래스의 멤버 변수로 할당
  postsService = new PostsService();

  // 게시물 목록 조회 메서드
  getPosts = async (req, res, next) => {
    try {
      // 서비스 계층에 구현된 findAllPosts 로직을 실행합니다.
      const posts = await this.postsService.findAllPosts();

      return res.status(200).json({ data: posts });
    } catch (err) {
      next(err);
    }
  };

  // 게시물 상세 조회 메서드
  getPostDetail = async (req, res, next) => {
    try {
      const { postId } = req.params;

      // 게시물 상세 조회
      const post = await this.postsService.findOnePost(+postId);

      return res.status(200).json({ data: post });
    } catch (err) {
      next(err);
    }
  };

  // 게시물 생성 메서드
  createPost = async (req, res, next) => {
    try {
      const { nickname, password, title, content } = req.body;

      // 서비스 계층에 구현된 createPost 로직을 실행합니다.
      const createdPost = await this.postsService.createPost(
        nickname,
        password,
        title,
        content
      );

      return res.status(201).json({ data: createdPost });
    } catch (err) {
      next(err);
    }
  };

  // 게시물 수정 메서드
  updatePost = async (req, res, next) => {
    try {
      const { postId } = req.params;
      const { password, title, content } = req.body;

      // 게시물 수정
      const updatedPost = await this.postsService.updatePost(
        +postId,
        password,
        title,
        content
      );

      return res.status(201).json({ data: updatedPost });
    } catch (err) {
      next(err);
    }
  };

  // 게시물 삭제 메서드
  deletePost = async (req, res, next) => {
    try {
      const { postId } = req.params;
      const { password } = req.body;

      // 게시물 삭제
      const deletedPost = await this.postsService.deletePost(+postId, password);

      return res.status(201).json({ data: { deletedPost } });
    } catch (err) {
      next(err);
    }
  };
}

✏️ 서비스 (Service)

  • 비즈니스 로직 계층(Business logic layer)은 아키텍처의 가장 핵심적인 비즈니스 로직을 수행하고 클라이언트가 원하는 요구사항을 구현하는 계층

  • 즉, 사용자의 요구사항을 주로 처리하는 실세 중에 실세

  • 프레젠테이션 계층(Presentation Layer)과 데이터 엑세스 계층(Data Access Layer) 사이에서 중간 다리 역할

  • 컨트롤러가 데이터를 요구할 때 저장소(Repository)에게 데이터를 요청함

  • 어플리케이션의 규모가 커질수록, 서비스 계층의 역할과 코드의 복잡성도 점점 커짐

// src/services/posts.service.js

import { PostsRepository } from '../repositories/posts.repository.js';

// 핵심적인 비즈니스 로직을 수행하는 클래스
export class PostsService {
  postsRepository = new PostsRepository();

  // 모든 게시물 조회 메서드
  findAllPosts = async () => {
    const posts = await this.postsRepository.findAllPosts();

    // 게시글을 생성 날짜로 부터 내림차순 정렬
    posts.sort((a, b) => {
      return b.createdAt - a.createdAt;
    });

    // password, content를 뺀 상태로, Controller에게 Response를 전달
    return posts.map((post) => {
      return {
        postId: post.postId,
        nickname: post.nickname,
        title: post.title,
        createdAt: post.createdAt,
        updatedAt: post.updatedAt,
      };
    });
  };

  // 특정 게시물 조회 메서드
  findOnePost = async (postId) => {
    const post = await this.postsRepository.findOnePost(postId);

    return {
      postId,
      nickname: post.nickname,
      title: post.title,
      content: post.content,
      createdAt: post.createdAt,
      updatedAt: post.updatedAt,
    };
  };

  // 게시물 생성 메서드
  createPost = async (nickname, password, title, content) => {
    const createdPost = await this.postsRepository.createPost(
      nickname,
      password,
      title,
      content
    );

    return {
      postsId: createdPost.postId,
      nickname: createdPost.nickname,
      title: createdPost.title,
      content: createdPost.content,
      createdAt: createdPost.createdAt,
      updatedAt: createdPost.updatedAt,
    };
  };

  // 게시물 수정 메서드
  updatePost = async (postId, password, title, content) => {
    // postId에 해당하는 게시물이 있는지 체크
    const post = await this.postsRepository.findOnePost(postId);
    if (!post) throw new Error('존재하지 않는 게시글입니다.');

    await this.postsRepository.updatePost(postId, password, title, content);

    const updatedPost = await this.postsRepository.findOnePost(postId);

    return {
      postId,
      nickname: updatedPost.nickname,
      title: updatedPost.title,
      content: updatedPost.content,
      createdAt: updatedPost.createdAt,
      updatedAt: updatedPost.updatedAt,
    };
  };

  // 게시물 삭제 메서드
  deletePost = async (postId, password) => {
    // postId에 해당하는 게시물이 있는지 체크
    const post = await this.postsRepository.findOnePost(postId);
    if (!post) throw new Error('존재하지 않는 게시글입니다.');

    await this.postsRepository.deletePost(postId, password);

    return {
      postId,
      nickname: post.nickname,
      title: post.title,
      content: post.content,
      createdAt: post.createdAt,
      updatedAt: post.updatedAt,
    };
  };
}

✏️ 저장소 (Repository)

  • 데이터 엑세스 계층(Data Access Layer)은 주로 데이터베이스와 관련된 작업을 처리하는 계층

  • 실제로 데이터베이스에 접근해서 데이터를 CRUD하는 계층

  • 컨트롤러와 서비스를 통해 전달 받은 데이터를 처리 후 반환함

// src/services/posts.service.js

import { prisma } from '../utils/prisma/index.js';

// 실제로 데이터베이스에 접근해서 데이터를 CRUD하는 클래스
export class PostsRepository {
  // 모든 게시물 조회 메서드
  findAllPosts = async () => {
    const posts = await prisma.posts.findMany();

    return posts;
  };

  // 특정 게시물 조회 메서드
  findOnePost = async (postId) => {
    const post = await prisma.posts.findFirst({
      where: { postId },
    });

    return post;
  };

  // 게시물 생성 메서드
  createPost = async (nickname, password, title, content) => {
    const createdPost = await prisma.posts.create({
      data: {
        nickname,
        password,
        title,
        content,
      },
    });

    return createdPost;
  };

  // 게시물 수정 메서드
  updatePost = async (postId, password, title, content) => {
    const updatedPost = await prisma.posts.update({
      where: { postId, password },
      data: { title, content },
    });

    return updatedPost;
  };

  // 게시물 삭제 메서드
  deletePost = async (postId, password) => {
    const deletedPost = await prisma.posts.delete({
      where: { postId, password },
    });

    return deletedPost;
  };
}


📌 Tomorrow's Goal

✏️ Node.js 심화 개인과제

  • 오늘 강의를 다 끝냈으니 내일부터 본격적으로 개인과제를 할 예정

  • 우선 기존의 코드 일부분을 리팩토링 할 예정

  • 그 다음에 Layered Architecture Pattern를 적용해서 코드 모듈화 진행



📌 Today's Goal I Done

✔️ Node.js 심화 강의 시청

  • 오늘은 어제 다 듣지 못한 Layered Architecture Pattern에 관한 내용을 학습함

  • 강의 중에 있는 퀴즈 문제를 풀 때 생각보다 구현이 헷갈렸음

  • 원래 함께 구현하던 코드들을 나눠서 구현하니 아직은 복잡하다고 느껴짐

  • router -> controller -> service -> repository -> database

  • 위 순서를 잘 기억해서 데이터의 CRUD를 구현해 보자!



📌 참고 자료

profile
조금씩 정리하자!!!

0개의 댓글