2023.06.22 TIL

0

TIL

목록 보기
35/37
post-thumbnail

오늘의 나는 무엇을 잘했을까?

오늘은 이론적인 내용과 실용적인 내용 둘 다 잘 배웠다. 특히 막연하게 알고있었던 배포 과정과 도커 컨테이너를 어떻게 배포하는지 알게되었다.

오늘의 나는 무엇을 배웠을까?

전통적 ssr과 ssr+hydration의 차이

  • 기존 ssr방식(php, asp.net등)
    • 서버에서 풀 페이지로딩을 한다.
    • 페이지를 이동할 때 페이지에 필요한 모든 문서(html, js, img등)를 서버가 다시 브라우저에게 준다.
  • ssr + hydration (next.js예시)
    • 처음 접속할 때(사이트의 entry point)의 페이지만 서버가 html을 렌더링한다.
    • 이후 hydration이 진행되어 react가 브라우저에서 VirtualDOM을 만든다. - 처음 들어간 페이지에서 이동할 수 있는 링크된 다른 페이지에 대한 js를 로드한다. (사이트 내 모든 페이지에 대한 js가 아님)
    • hydration이 끝나고 사용자 인터랙션을 할 수 있다.
    • 초기 페이지에서 다른 페이지로 이동할 시 서버에서 목적지 페이지에 대한 html을 렌더링하는게 아니라 초기 페이지 로딩 시 불러왔던 js chunk를 가지고 브라우저가 html을 만들어낸다. 맨 처음 이후부터는 CSR방식인 것이다. 그래서 기존 mpa처럼 페이지 이동시 다시 문서를 불러오는것이 아니나 spa처럼 부드러운 화면전환이 가능하다.

웹 배포의 과정

배포란 말 그대로 내 소스코드를 사람들에게 배포하는 것이다.

사실 복잡한 것이 아니라 로컬 컴퓨터로 서버를 run 또는 node 명령과 같은 명령으로 실행시키는 것과 같은데, 로컬 컴퓨터로 하면 계속 켜놔야 하고 방화벽 설정을 직접 다 해야하는 번거로움 및 해킹에 취약하다는 단점 때문에 클라우드 컴퓨팅 서비스를 사용하여 내 로컬 컴퓨터가 아닌 업체의 컴퓨터를 빌려 서버를 실행시켜 놓는것이다.

그럼 node.js로 만든 서버를 배포하는 과정을 예시로 들어보자. 먼저 로컬 컴퓨터에서 서버를 구동하고 배포하려고 하면, main.js에 소스코드를 작성하고 터미널에 해당 프로젝트 디렉토리로 들어가서 node main 이라는 명령어를 치고, 서버에서 예를들자면 listening on port xxxx이라는 문구가 뜰 것이다. 이렇게 해서 배포가 끝났다. (사실 이렇게 실행시킨다고 다른 컴퓨터에서 내 서버에 요청을 날릴순 없다. 웹 서버를 구동하기 위한 추가적인 절차가 필요하다)

그러면 이번엔 로컬 컴퓨터에서 소스코드를 작성한 뒤에 클라우드 서비스를 이용하여 배포하는 과정을 알아보자. EC2 인스턴스를 빌린다. EC2인스턴스는 컴퓨터이다. 그 컴퓨터에 ssh로 접속할 수 있다. 접속하고 난 뒤 소스코드가 필요하니 github에 올려둔 소스코드를 clone한다. (처음 ec2에 접속하면 git도 없으니 git도 설치하고, 여러가지 필요한 것들을 설치해준다) 그 다음 로컬 컴퓨터에서 했듯이 클론한 리포지토리로 가서 npm install 등을 실행해주고, node main을 쳐서 서버를 실행시킨다. 이렇게 배포가 끝난다.
만약 소스코드를 업데이트 한다면 ec2에 접속해서 git pull을 통해 업데이트된 소스코드를 다시 받아주고 새로운 패키지를 설치했다면 npm install도 해주는 등 추가적인 명령들을 실행한 후 다시 node main을 통해 서버를 구동할 것이다.

이거를 소스코드가 변경될 때마다 사람이 일일히 해야한다면 상당히 귀찮을 것이다. 그래서 ci/cd를 통한 배포 자동화가 필요한 것이다.

Mongoose

  • ODM: Object document mapper, db 데이터를 코드에서 객체로 매핑해주는 툴
  • ORM: Object relational mapper, 대표적으로 typeorm, sequalize
  • Mongoose는 node에서 도큐먼트를 객체로 매핑해주는 툴(ODM)
  • 설치
     npm install mongoose
  • db를 연결하기 위한 dbConnect.ts파일 생성 (https://github.com/vercel/next.js/tree/canary/examples/with-mongodb-mongoose)여기서 dbConnect.ts파일 참고해도 됨
    import mongoose from 'mongoose'
     
     const MONGODB_URI = process.env.MONGODB_URI
     
     if (!MONGODB_URI) {
       throw new Error(
         'Please define the MONGODB_URI environment variable inside .env.local'
       )
     }
     
     /**
      * Global is used here to maintain a cached connection across hot reloads
      * in development. This prevents connections growing exponentially
      * during API Route usage.
      */
     let cached = global.mongoose
     
     if (!cached) {
       cached = global.mongoose = { conn: null, promise: null }
     }
     
     async function dbConnect() {
       if (cached.conn) {
         return cached.conn
       }
     
       if (!cached.promise) {
         const opts = {
           bufferCommands: false,
         }
     
         cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
           return mongoose
         })
       }
     
       try {
         cached.conn = await cached.promise
       } catch (e) {
         cached.promise = null
         throw e
       }
     
       return cached.conn
     }
     
     export default dbConnect
  • .env.local 파일에 MONGODB_URI값 입력
    • mongoDB atlas에서 connect→vscode선택하면 url나옴. 그걸로 세팅해주면 됨
  • 스키마(혹은 모델)생성
    import { PostCategory } from "@/app/types/post";
     import mongoose from "mongoose";
     
     const PostSchema = new mongoose.Schema(
       {
         title: {
           type: String,
           required: true,
         },
         contents: String,
         category: {
           type: String,
           enum: PostCategory,
           default: PostCategory.General,
         },
         writeId: mongoose.Schema.Types.ObjectId,
       },
       { timestamps: true }
     );
     
     export const PostModel =
       mongoose.models.Post || mongoose.model("Post", PostSchema);
  • post 조회 API생성
    import dbConnect from "@/dbConnect";
     import { PostModel } from "@/lib/api/models/post";
     import { NextRequest, NextResponse } from "next/server";
     
     export const GET = async (req: NextRequest) => {
       await dbConnect();
       const posts = PostModel.find();
       return NextResponse.json(posts);
     };
     
     export const POST = async (req: NextRequest) => {
       await dbConnect();
       const body = await req.json();
     
       const post = await PostModel.create(body);
       return NextResponse.json(post);
     };
  • 개별 post 조회 api만들기
    import dbConnect from "@/dbConnect";
     import { PostModel } from "@/lib/api/models/post";
     import { NextRequest, NextResponse } from "next/server";
     
     export const GET = async (
       req: NextRequest,
       { params }: { params: { postId: string } }
     ) => {
       await dbConnect();
       const postId = params.postId;
       const post = await PostModel.findOne({ _id: postId });
       return NextResponse.json(post);
     };
     
     // post 수정 api
     export const POST = async (
       req: NextRequest,
       { params }: { params: { postId: string } }
     ) => {
       await dbConnect();
       const body = req.json();
       const postId = params.postId;
       const post = await PostModel.findOneAndUpdate({ _id: postId }, body, {
         new: true,
       });
       return NextResponse.json(post);
     };
     
     // post 삭제 api
     export const DELETE = async (
       req: NextRequest,
       { params }: { params: { postId: string } }
     ) => {
       await dbConnect();
       const postId = params.postId;
       const post = await PostModel.findByIdAndDelete(postId);
       return NextResponse.json(post);
     };
  • mongoose 심화
    • sorting
    • filtering
      import dbConnect from "@/dbConnect";
       import { PostModel } from "@/lib/api/models/post";
       import { NextRequest, NextResponse } from "next/server";
       
       enum PostOrder {
         CreatedAtAsc = "CREATED_AT_ASC",
         CreatedAtDesc = "CREATED_AT_DESC",
       }
       
       const getSortOrder = (orderBy: string): { createdAt: 1 | -1 } => {
         switch (orderBy) {
           case PostOrder.CreatedAtAsc:
             return { createdAt: 1 };
           case PostOrder.CreatedAtDesc:
             return { createdAt: -1 };
           default:
             return { createdAt: -1 };
         }
       };
       
       export const GET = async (req: NextRequest) => {
         // const query = new URL(req.url);
         const searchParams = req.nextUrl.searchParams;
         const orderBy = searchParams.get("orderBy");
         const category = searchParams.get("category");
         const filter = {};
         if (category) {
           Object.assign(filter, { category });
         }
         await dbConnect();
         const posts = await PostModel.find(filter).sort(
           getSortOrder(orderBy as string)
         );
         return NextResponse.json(posts);
       };
       
    • count
    • aggregation

오늘의 나는 어떤 어려움이 있었을까?

발표자료를 준비하는데 이론적인 개념이 헷갈려서 힘들었다. 계속 찾아보고 물어보고 있지만 아직 헷갈리는 개념이 너무 많고 용어도 정확하게 알려주는 곳이 잘 없다.

내일의 나는 무엇을 해야할까?

  • 발표자료 준비
  • 위클리 미션

0개의 댓글