Skip to content

10월 31일 회의

lee0jae330 edited this page Nov 1, 2024 · 2 revisions

할 일

# 숙제 검사

  • TMI 한 가지씩 준비 (구두)
  • 정해진 기술 스택 장단점 조사해오기
  • ERD 어떻게 짜는지 알아오기
  • 아키텍쳐 어떤게 좋을지 알아오기

# 10.31(목)

  • 발표 준비(1시반 ~ 3시 반까지)
  • 멘토링 일지 작성(4시 ~ 6시)

  • 아키텍처
  • 데이터 저장 방식
  • 디자인 마무리
  • 파트 나누기
  • 초기 세팅
    • 라이브러리 설치
    • 깃허브
    • 기술 선택 기술적 근거 정리

숙제

  • 권나연

    정해진 기술 스택 장단점 조사해오기

    FE

    Typescript

      TypeScript JavaScript
    장점 타입 안정성  
    코드 가독성 → 유지보수성 브라우저에서 즉시 실행 가능  
    단점 컴파일 필요로 빌드 시간 증가  
    초기 설정이 복잡 타입 안전성 부족  

    아키텍쳐 어떤게 좋을지 알아오기

    project-root
    ├── client                # 프론트엔드 폴더 (React/Vite 등)
    │   ├── public            # 정적 파일 (이미지, 폰트 등)
    │   ├── src
    │   │   ├── assets        # 이미지, CSS 파일 등 리소스
    │   │   ├── components    # 재사용 가능한 UI 컴포넌트
    │   │   ├── pages         # 페이지 단위 컴포넌트
    │   │   ├── hooks         # 커스텀 훅
    │   │   ├── services      # API 호출 로직 (axios 인스턴스 및 API 함수)
    │   │   ├── stores        # 상태 관리 관련
    │   │   ├── App.tsx       # 애플리케이션의 진입 컴포넌트
    │   │   ├── main.tsx      # ReactDOM 렌더링 진입점
    │   │   └── vite.config.ts # Vite 설정 파일
    ├── server                # 백엔드 폴더 (Express/NestJS 등)
    │   ├── config            # 설정 파일 (DB, 서버 설정 등)
    │   ├── controllers       # 라우터에서 호출되는 컨트롤러 함수
    │   ├── models            # Mongoose 모델 정의
    │   ├── routes            # API 라우터 정의
    │   ├── middlewares       # 미들웨어 (인증, 에러 처리 등)
    │   ├── services          # 비즈니스 로직 처리 함수 (데이터베이스 조작 등)
    │   ├── utils             # 유틸리티 함수 (포맷팅, 검증 함수 등)
    │   ├── app.ts            # 서버 구성 (Express 애플리케이션)
    │   ├── index.ts          # 서버 시작점
    │   └── .env              # 환경 변수 파일
    ├── shared                # 프론트엔드와 백엔드에서 공유할 코드
    │   ├── types             # TypeScript 인터페이스 및 타입 정의
    │   └── constants         # 상수 정의 (예: URL, 에러 메시지 등)
    ├── docker-compose.yml    # Docker 설정 파일
    ├── Dockerfile            # 서버의 Dockerfile
    ├── nginx.conf            # Nginx 설정 파일 (리버스 프록시 설정)
    ├── .gitignore            # Git 무시 파일 목록
    └── README.md             # 프로젝트 설명 문서
    

    ERD 어떻게 짜는지 알아오기

    서버 저장 필요

    • 학습 컨텐츠 데이터
    • 특정 회원의 학습 컨텐츠 진행도
    • 특정 회원의 워크스페이스

    작업

    • 블록 조립 상태 → XML
    • 워크스페이스ID
    erDiagram
        USER {
            int id PK
            string name
            string email
            datetime created_at
            string status
        }
    
    LEARNING_INFO {
        int id PK
        string title
        text content
        string difficulty
        string tech_tags
        
    }
    
    LEARNING_STEP {
        int id PK
        int learning_info_id FK
        text answer
    }
    
    SUCCESSFUL_LEARNING_STEP {
        int id PK
        int user_id FK
        int learning_step_id FK
        datetime succeed_at
    }
    
    WORKSPACE {
        int id PK
        string name
        int user_id FK
        datetime edited_at
        text preview
        text block_code
        string status
    }
    
    USER ||--o{ WORKSPACE : "owns"
    LEARNING_INFO ||--o{ LEARNING_STEP : "has"
    USER ||--o{ SUCCESSFUL_LEARNING_STEP : "attempts"
    LEARNING_STEP ||--o{ SUCCESSFUL_LEARNING_STEP : "is part of"
    

    학습 컨텐츠

    • 따로 ENUM 테이블 분리하지 말고 ‘ ‘으로 구분?

      ex) HTML CSS

    학습 컨텐츠 진행도

    학습 컨텐츠 성공한 단계

    • 진행도

      0 ~ 100

    • 성공한 횟수

    워크스페이스

    • preview/ block code

      img or xml

    참고

    https://velog.io/@dipokalhhj/Abandoned-React

    https://blog.nextinnovation.kr/tech/Vue_React_Angular/

    https://velog.io/@leehaeun0/React-vs-Vue-장단점-비교

    https://www.elancer.co.kr/blog/detail/171

    https://tech.kakaoenterprise.com/109

    https://techblog.woowahan.com/16910/

    https://bumang.blog/99

    https://blog.hectodata.co.kr/bonjour-vite/

    https://blog.hwahae.co.kr/all/tech/6099

  • 이영재

    • 정해진 기술 스택 장단점 조사해오기

      React

      장점

      • 공식 문서가 아주 자세히 작성되어 있다.
      • 방대한 커뮤니티, 자료를 통해 쉽게 접하고 배울 수 있음
      • Component 단위로 관리를 하기 때문에 간편함
      • UI 수정 쉽고 커스텀 훅을 통해 코드의 재사용성을 높힐 수 있음
      • 리액트 생태계가 잘 이루어져있음
        • 리액트와 호환되는 많은 라이브러리
          • React Query
          • React Router
          • Redux 등등
        • 다양한 프레임워크 지원
          • Nextjs
          • Gatsby
          • Remix 등등
      • Virtual DOM을 사용해 UI 변경 시 실제 DOM 대신 가상 DOM에서 먼저 렌더링을 처리함 → 이를 통해 실제 DOM 조작을 최소화하여 성능을 향상시킴

      단점

      • IE8 이하 버전은 지원하지 않음
      • 데이터 모델링, 라우팅, ajax 등 기능 지원이 안됨
      • Javascript, JSX 배경지식이 필수적이다
      • 초기 설정이 복잡함
        • 리액트는 UI 라이브러리라서 라우팅, 상태 관리, 빌드 설정 등의 여러 가지 설정이 필요함
      • 프론트엔드 단점과 같은 트렌드가 너무 빨리 변함.
        • 새로운 버전이 빠르게 나와서 최신 트렌드를 따라갈라면 꾸준한 학습 필요
      • SEO 문제
        • 리액트는 클라이언트 사이드 렌더링을 사용하여 검색 엔진이 자바스크립트를 해석하지 못하는 경우 SEO에 불리할 수 있음
        • SEO 문제는 서버 사이드 렌더링을 지원하는 NextJS를 사용하면 극복할 수 있음
      • 상태 관리가 복잡함
        • 컴포넌트 기반 구조에서 여러 컴포넌트 간의 상태를 관리하는 것은 많이 복잡함
        • 다양한 상태 관리 라이브러리가 있지만 이를 잘 다루기 위해서는 경험을 많이 쌓아야함

      Vite

      장점

      • 빠른 개발 서버
        • ESM을 기반으로 서버를 구동하여, 초기 로딩이 빠름
      • HMR (Hot Module Replacement)
        • Vite는 변경된 모듈만 갱신하기 때문에 페이지 전체를 다시 로드할 필요가 없으며, 변경 사항을 실시간으로 반영하여 개발 생산성을 높힘
      • 간편한 설정과 플러그인 시스템
        • Vite는 기본 설정이 간단함
        • 플러그인 시스템을 통해 쉽게 기능을 확장할 수 있음
      • 번들링과 빌드 속도가 빠름
        • vite는 빌드 단계에서 Rollup을 사용하여 최적화된 번들링을 수행함
        • 코드 스플리팅, 트리 셰이킹, 압축 등의 작업이 Rollup으로 최적화되어, 배포 환경 빌드도 매우 빠르게 수행할 수 있음
      • 다양한 프레임워크와 호환됨
        • Vite는 React, Vue, Svelte 등 여러 프레임워크와 호환이 됨

      단점

      • 레거시 브라우저 지원에 한계가 있음
        • Vite는 최신 ESM을 기반으로 하기에, IE11 같은 레거시(구형) 브라우저에서는 호환성 문제가 있을 수 있음
      • 플러그인 호환성 문제
        • Vite는 Webpack에 비하면 최신 번들러이기 때문에, Webpack에서 사용하는 특정 플러그인이나 로더가 Vite와 호환되지 않을 수 있음
      • Webpack에 비해 생태계가 작음
        • 비교적 최신에 나온 번들러이기 때문에 역사가 깊은 Webpack에 비해 커뮤니티가 작고 자료가 적음
      • 대규모 프로젝트 지원에 한계가 있을 수 있음
        • Vite는 대규모 프로젝트에서도 사용 가능하지만 파일 의존성이 매우 복잡한 경우 빌드 속도가 상대적으로 느려질 수 있음

      Typescript

      장점

      • 자바스크립트의 슈퍼셋

        • 기존 자바스크립트와 완벽하게 호환됨
        • 기존 자바스크립트 프로젝트에 타입스크립트를 점진적으로 적용해서 마이그레이션할 수 있음
        • Ts와 Js를 혼용해도 문제가 없음
      • 컴파일 언어 + 정적 언어임

        • 컴파일 과정에서 오류를 찾을 수 있음
        • TS는 타입을 명시하여 개발 단계에서의 안정성을 높여줌
        • 타입을 미리 체크하기에 JS에 비해 실행 시간이 빠름
      • 객체 지향 가능

        • 자바스크립트로도 객체 지향 프로그래밍이 가능함. 하지만 타입스크립트는 class에서 private 기능 지원, interface, type 등을 통해 더 쉽게 객체 지향 프로그래밍을 할 수 있음

        단점

        • 러닝커브

          • 자바스크립트에 익숙해져 있으면 타입을 익히고 적절하게 지정하는데 시간이 걸림
          • interface, type, enum 등 자바스크립트에서는 없는 기능이 추가됨
        • 가독성 저하

          • 기존 자바스크립트 코드에 타입을 선언하고 지정하는 과정에서 코드의 수가 증가함

            → 이걸 부정적으로 보는 개발자들이 있음

        • any 남발

          • type에 익숙치 않으면 any를 남발하는데 이는 타입스크립트의 장점인 타입 지정을 완전 무시하고 쓰는 꼴임

            → 안쓰는 것보다 못하는 꼴이 되어버림

      Tailwind Css

      장점

      • zero-runtime CSS
        • 런타임이 아닌 빌드 시점에 CSS를 생성하기 때문에 페이지 로딩 시 불필요한 계산이 줄어들어 성능이 개선됨
        • CSR 뿐만 아니라 SSR에 적용할 수 있음
          • 서버에서 미리 생성된 CSS를 클라이언트에 전달할 수 있음
      • CSS를 작성할 때 시간을 절약할 수 있음
        • css module, js in css를 사용할 때는 클래스명을 짓느라 시간을 보낼 때가 있는데 tailwind css는 그냥 사전에 정의된 class만 사용하면 돼서 편함
      • 커스터 마이징이 쉬워 원하는대로 디자인을 변경할 수 있음
        • tailwind.config.js를 설정하거나 w-[2.5rem], w-[720px] 같이 원하는 값을 넣어서 css를 적용할 수 있음
      • 반응형 디자인 구현이 쉬움
        • xl, lg, md, sm 같은 뷰포인트 클래스로 반응형을 쉽게 구현할 수 있음

      단점

      • 컴포넌트의 className이 너무 길어져서 가독성이 저하됨

        <hr className="w-3/4 min-w-[260px] border-dashed border-black lg:min-w-[490px]" />
        <div className="flex w-56 flex-col items-center border-b border-black">
        </div>
        
      • 새로운 스타일을 사용하면 CSS 클래스를 생성하게 되어 CSS 파일 크기가 커짐 → 페이지 로드 시간이 늘어날 가능성이 있음

      • 다른 개발자와 협업할 시 tailwind.config.js에서 충돌이 생길 수가 있음

      • tailwind css에서 특정 css를 지원하지 않는 경우 별도의 css 파일을 생성해야 하기에 일관성 있는 스타일링이 어려울 수 있음

      Zustand

      장점

      • 러닝 커브가 적음

        • zustand는 설정이 매우 간단하고, 상태를 생성하고 사용할 수 있는 API가 직관적임

        • create 함수로 상태를 정의하고 컴포넌트에서 바로 상태를 가져와 사용할 수 있어 코드가 깔끔함

        • 동작을 이해하기 위해 알아야 하는 코드 양이 매우 적음

          • 핵심 로직의 코드 줄 수가 약 42줄 밖에 되지 않음

          https://www.nextree.io/zustand/

      • 리액트와 호환이 잘됨

        • React의 context 시스템에 의존하지 않아서 불필요한 리렌더링을 줄임. 상태를 구독한 컴포넌트만 리렌더링되어 성능 최적화가 용이
      • 보일러 플레이트가 거의 없음

        • context api와 비교해보면 확실함
      • typescript 지원 !

      단점

      • 중대형 애플리케이션에 비해 제한된 기능
        • zustand는 간결함을 목표로 설계되었기에 Redux 같은 복잡한 상태 관리 라이브러리에서 지원하는 복잡한 상태 관리나 미들웨어 지원이 제한적임
      • 정식 DevTools 미지원
        • 기본적으로 Redux DevTools와 같은 공식 디버깅 도구를 지원하지 않아서 상태 변화를 추적하는 데 어려움이 있을 수 있음

      React Query (Tanstack Query)

      장점

      • 데이터 페칭과 캐싱 최적화

        • React Query는 데이터를 자동으로 캐싱하여, 같은 데이터를 여러 번 요청하지 않고 캐시에서 가져올 수 있도록 함

          이를 통해 불필요한 네트워크 요청을 줄이고 성능을 최적화할 수 있음

      • 자동 리페치

        • 데이터가 오래 되었거나 백그라운드에서 변경되면 자동으로 최신 데이터를 가져옴
        • 자동으로 데이터를 동기화해서 최신 상태를 유지할 수 있음
      • 페이징 및 무한 스크롤 지원

        • React query는 페이징과 무한 스크롤을 기본으로 제공하여, 많은 데이터가 필요할 때 유용함
        • 추가적인 설정이나 로직 구현이 불필요
      • Typescript와 호환이 잘됨

      • 서버 상태 관리 가능

        • 데이터 동기화, 캐싱, 자동 리페치, 캐시 무효화 등의 복잡한 서버 상태 관리를 제공함

      단점

      • 러닝 커브
        • React Query의 기능은 많고 설정할 수 있는 옵션이 다양해서 React Query를 처음 접한 사람들을 익히는데 시간이 많이 걸림
      • 클라이언트 상태 관리 부재
        • React Query는 서버 상태 관리에 최적화되어 있지만, 클라이언트 상태 관리에는 적합하지 않음
      • 캐시 메모리 관리 필요
        • 데이터를 캐싱하지만 오래된 캐시 데이터가 메모리에 남아 있을 수 있음
      • 동일한 데이터를 여러 컴포넌트에서 관리할 때, 각 컴포넌트가 다른 쿼리 키를 사용할 경우 데이터 일관성 문제가 발생할 수 있음

      Express

      장점

      • 경량 프레임워크
        • Express는 기본 기능만을 제공함으로 가볍고 유연함
        • 필요에 따라 다양한 미들웨어와 모듈을 추가해 맞춤형 서버를 구축할 수 있음
      • 간단하고 직관적인 API
        • Express는 HTTP 요청 및 응답을 다루기 쉽게 설계되어 있음
        • 또한 API가 간결하고 직관적임
      • 커뮤니티가 크고 다양한 라이브러리를 지원
        • Express는 nodejs 기반으로 오랫동안 사용된 프레임워크이기 때문에 많은 자료와 라이브러리가 존재함
      • 미들웨어를 통한 기능 확장
        • 미들웨어를 통해 인증, 데이터 유효성 검사, 로깅, 에러 핸들링 등 다양한 기능을 간단히 추가할 수 있음
      • 풀스택 개발 환경 지원
        • 자바스크립트 기반이기 때문에 풀스택으로 개발할 수 있음

      단점

      • 경량 프레임워크
        • 너무 유연해서 프로젝트 구조를 설계하고 아키텍처를 적용하는데 많은 시간이 걸림
      • 미들웨어 사용으로 인한 복잡성 증가
        • 기능 확장을 위해 미들웨어를 추가하는 것이 장점이기도 하지만, 많은 미들웨어를 사용하면 요청을 처리하는 로직이 복잡해지고 성능이 저하될 수 있음
      • 실시간 기능에 대한 한계
        • Express는 웹 소켓을 지원하지 않음
        • 실시간 데이터 처리가 필요한 경우 Socket.IO와 같은 별도의 라이브러리를 도입해야됨

      MongoDB

      장점

      • 유연한 스키마 구조

        • MongoDB는 스키마가 고정되지 않아 데이터 구조를 유연하게 변경할 수 있음
        • 관계형 데이터베이스와 달리 새로운 필드나 데이터를 추가할 때 미리 정의할 필요가 없음
      • 높은 확장성

        • MongoDB는 수평 확장을 지원하여 데이터가 많아지면 서버를 쉽게 추가할 수 있음
        💡수평 확장?
        서버의 자체적인 스펙을 업그레이드 하는 것이 아닌 서버의 개수를 늘리는 방법
        
      • 문서 지향 데이터 모델

        • JSON과 유사한 BSON (Binary JSON) 형식으로 데이터를 저장하므로, JavaScript 및 Node.js 환경과 호환이 잘 됨

      단점

      • 데이터 일관성 문제
        • MongoDB는 기본적으로 최종적 일관성 모델을 채택하고 있기에, 데이터가 모든 복제본에 즉시 동기화 되지 않을 수 있음
      • 복잡한 조인 연산의 한계
        • MongoDB는 SQL의 JOIN과 같은 복잡한 조인 연산을 기본적으로 지원하지 않아, 여러 컬렉션에 걸친 데이터를 다룰 때 불편할 수 있음
    • ERD 어떻게 짜는지 알아오기

      [MongoDB] NOSQL 데이터 모델링 기법 살펴보기

      ERD 다이어그램 그리는 방법 - 내일배움캠프 블로그

    • 아키텍쳐 어떤게 좋을지 알아오기

  • 이유진

    기술 장단점

    생각해보니까 저희 사용할 라이브러리도 총 정리해도 좋을듯.
    폴더 구조 잡을 때 고려해야해서!
    

    React

    • 장점: 프론트 프레임워크 중 1등. 커뮤니티가 잘 형성되어 있음.
    • 단점: -

    Typescript

    • 장점: 타입 안정성을 제공해서 코드의 오류를 줄인다. 유지 보수 및 협업에 유리하다.
    • 단점: 컴파일 시간이 생긴다.

    Vite

    • 장점: 설정이 매우 간편하다. HMR로 빌드 속도와 개발 환경이 빠르다. ESM을 사용해서 최신 브라우저에 적합하다.
    • 단점: 구형 브라우저 지원에 제약이 있고 webpack에 비해 플러그인이 적다.

    tailwind CSS

    • 장점: 유틸리티 기반으로 빠르게 스타일링을 할 수 있고 디자인의 일관성을 유지하기 좋다.
    • 단점: 코드의 가독성이 떨어지고 비표준 스타일링을 적용할 때 제약이 있을 수 있다.

    Zustand

    • 장점: 러닝커브가 적다.
    • 단점: redux에 비해 미들웨어 지원에서 부족하다. 커뮤니티가 적다.

    Tanstack-Query

    • 장점: 비동기 데이터 관리에 특화되어 있고, 데이터 캐싱과 최신성 유지가 자동으로 된다.
    • 단점: 데이터 캐싱 전략이 복잡할 수 있다.

    Express

    • 장점: 커뮤니티가 활발하고 경량 서버라 node.js와 잘 결합되어 빠른 응답을 받을 수 있다.
    • 단점: 대규모 프로젝트에서는 관리가 어려울 수 있다.

    MongoDB

    • 장점: 유연한 스키마를 저장할 수 있고 json 형식으로 데이터를 저장해 javascript와 연동하기 쉽다. 확장성이 좋다.
    • 단점: 데이터 관계가 복잡하면 스키마를 나누기 어려울 수 있다.

    MongoDB Schema Design

    Collection

    • User: 사용자 정보

    • Workspaces: workspace에 대한 정보

      {
        "_id": "ObjectId",                 // 워크스페이스 고유 ID
        "name": "String",                  // 워크스페이스 이름
        "creatorId": "ObjectId",           // 생성자 ID (사용자 컬렉션의 _id 참조)
        "createdAt": "Date",               // 생성일자
        "updatedAt": "Date",               // 수정일자
        "isLearning": "Boolean",           // 학습용 여부 (true/false)
      

      "learningData": { // 학습용 데이터 "isCompleted": "Boolean", // 학습 완료 여부 "answerBlockIds": ["ObjectId"], // 정답 블록 ID 목록 "progressStage": "number" // 진행 단계 },

      "collaboration": { // 동시 편집 관련 정보 "activeUsers": ["ObjectId"], // 현재 접속 중인 사용자 ID 리스트 "isShared": "Boolean" // 공유 여부 } }

    • Blocks: 블록 데이터(변경 사항이 많기 때문에 다른 콜렉션으로 분류, 정답 블록 포함)

      {
        "_id": "ObjectId",           // 블록 세트의 고유 ID
        "workspaceId": "ObjectId",   // 이 블록 세트가 속한 workspace의 ID
        "blocksData": "<XML or JSON>",  // Blockly에서 생성한 XML 혹은 JSON 포맷의 블록 데이터
        "createdAt": "Date",         // 블록 세트가 생성된 날짜
        "updatedAt": "Date",         // 블록 세트가 마지막으로 업데이트된 날짜
      }
      

    https://www.youtube.com/watch?v=QAqK-R9HUhc

    아키텍처

    FE - FSD

    image.png

    • layers → slice(도메인) → segments
    • 폴더의 depth가 3단계로 제한됨
    • 위에서 아래로 import는 되지만 아래에서 위로 import는 안됨 → 위계질서가 생김
    • 일단 어려움. 그리고 적응 기간이 있음. 초기에는 오래 걸릴 수 있음. 전에 써본 분이 욕한 기억이… 팀원 모두가 규칙을 잘 지켜야 함. 안그러면 의미가 없는 아키텍처
    • processes는 사라짐
    • 구조
      • app(전역 컴포넌트 app.tsx)

      • page

      • widgets(component - 재사용 기준)

        image.png

      • features(기능)

      • entities(데이터)

      • shared(utiles, hooks, …) → slices가 없음

    https://www.youtube.com/watch?v=64Fx5Y1gEOA

    FE - atomic + component/container 패턴

    https://yozm.wishket.com/magazine/detail/1531/

    https://yozm.wishket.com/magazine/detail/1531/

    • 컴포넌트를 구현할 때 많은 시간이 소요
    • 5 단계로 컴포넌트를 분리할 때 어디로 분리하는 게 맞는지 고민이 많이 들어감(이름 정하는 것처럼)
    • props drilling이 심함

    • 그러나 atomic 패턴이 없으면 페이지 단으로 컴포넌트를 분리하기엔 컴포넌트가 너무 많을 수 있음
    • storybook을 적용한다면 storybook과 잘 맞아 떨어지는 패턴
    • atomic을 부분 적용하는 것 제안
      • Molecules / Organisms / Pages

      • Molecules→ 최소 1가지 기능을 수행 (atoms랑 molecules 합치기)

        tab, button, workspace …

      • Organisms → 사용자에게 의미와 역할이 존재하는 단위

        previewTabSection, cssTabSection, workspaceListSection …

      • Pages → 최종 페이지

    src/
    ├── assets/                  # 이미지, SVG, 폰트 등 정적 자산
    │   ├── logo.svg
    │   └── ...
    │
    ├── components/              # 재사용 가능한 UI 컴포넌트 모음
    │   ├── Molecules/           # 1개 이상의 기능을 하는 컴포넌트
    │   │   ├── Button.tsx
    │   │   ├── tab.tsx
    │   │   └── ...
    │   ├── Organisms/           # Molecules를 조합하여 특정 기능을 갖춘 컴포넌트
    │   │   ├── previewTabSection.tsx
    │   │   ├── cssTabSection.tsx
    │   │   └── ...
    │   └── ...
    │
    ├── hooks/                   # 커스텀 훅
    │   ├── useAuth.ts
    │   ├── useFetchData.ts
    │   └── ...
    │
    ├── pages/                   # 주요 페이지 컴포넌트 (라우팅되는 컴포넌트)
    │   ├── Home.tsx
    │   ├── About.tsx
    │   ├── Contact.tsx
    │   └── ...
    │
    ├── stores/                  # Zustand 상태 관리
    │   ├── useAuthStore.ts      # 인증 관련 상태 관리
    │   ├── useUserStore.ts      # 사용자 관련 상태 관리
    │   └── ...
    │
    ├── services/                # API 요청과 관련된 서비스
    │   ├── apiClient.ts         # axios 등 API 클라이언트 설정
    │   ├── userService.ts       # 사용자 관련 API 요청 함수
    │   ├── productService.ts    # 제품 관련 API 요청 함수
    │   └── ...
    │
    ├── styles/                  # 전역 스타일과 Tailwind 설정
    │
    ├── types/                   # 공용 타입 정의
    │   ├── authTypes.ts
    │   ├── userTypes.ts
    │   └── ...
    │
    ├── utils/                   # 유틸리티 함수
    │   ├── formatDate.ts
    │   ├── validateEmail.ts
    │   └── ...
    │
    ├── App.tsx                  # 최상위 컴포넌트
    ├── main.tsx                 # Vite의 엔트리 파일
    └── vite-env.d.ts            # Vite 환경 변수 타입 정의
    

    BE - MVC

    https://github.com/hagopj13/node-express-boilerplate

    src\\
     |--config\\         # Environment variables and configuration related things
     |--controllers\\    # Route controllers (controller layer)
     |--docs\\           # Swagger files
     |--middlewares\\    # Custom express middlewares
     |--models\\         # Mongoose models (data layer)
     |--routes\\         # Routes
     |--services\\       # Business logic (service layer)
     |--utils\\          # Utility classes and functions
     |--validations\\    # Request data validation schemas
     |--app.js          # Express app
     |--index.js        # App entry point
    
  • 최경일

    • 정해진 기술 스택 장단점 조사해오기
      • FE: React + TypeScript + Vite
        • React: 컴포넌트 기반 UI 설계로 재사용성 높음. 가상돔을 통한 성능 최적화. 다양한 라이브러리 있음. 단점은… 점점 없어지는 느낌이다.
        • TypeScript: 정적 타입 검사로 코드 안전성 향상. 러닝커브 및 코드 작성 속도저하가 있지만 하는게 필수인 트렌드(안하는 기업도 있긴 하지만, 우리는 학습목적)
        • Vite: 번들속도가 빠르다. 확장성이 webpack보다 적을 수 있다고는 하는데 요즘은 아닌듯?
      • CSS: Tailwind CSS
        • 장점: 클래스 이름 안짓기가 정말 크다. 개발 속도 빠르다. 런타임이 아니라 빌드타임에 스타일을 적용하고, 불필요한 css는 제거해서 초기 로딩 속도가 빠르다. (Styled-Components는 런타임에 스타일 생성)
        • 단점: 가독성이 문제이지만, 초기설정 및 세팅으로 조금 줄일 수는 있다. 동적인 스타일링이 조금 불편하다.
      • 상태 관리: Zustand + Tanstack-Query
        • Zustand: 사용이 상대적으로 쉬운 전역상태관리. 상태관리가 복잡해지면 힘들 수 있다.
        • Tanstack-Query: 데이터 패칭, 캐싱, 상태 관리 등을 편하게 처리한다. 서버와의 동기화가 쉬운 서버 상태 관리 라이브러리. 캐싱이 메모리 기반으로 새로고침 시 상태가 초기화되고, 학습곡선이 조금 있는게 단점.
      • BE: Express + MongoDB + Mongoose
        • Express: 설정이 간단하고, 미들웨어와 라우팅 쉽게 관리. 큰 규모 프로젝트에는 추가 설정이 필요함.
        • MongoDB: NoSQL로 유연한 데이터 모델링 가능. JSON과 비슷한 BSON 형식. 확장성이 좋음. 복잡한 쿼리에서는 SQL에 비해서는 제한적
        • Mongoose: Node.js 환경에서 MongoDB와 상호작용 하기 위한 ODM(Object Data Modeling) 라이브러리
      • 배포: nginx + Docker + NCP + GitHub Actions
        • nginx: 웹 서버로서 정적 파일(HTML, CSS, JS, 이미지)을 빠르게 제공한다. 또한 리버스 프록시로 클라이언트 요청을 적절한 서버로 분배하는 역할. 배포시에는 웹 애플리케이션의 속도와 안전성을 높이기 위한 중요한 역할. 복잡한 설정 + 학습 곡선이라는 단점.
        • Docker: 환경을 하나의 이미지로 패키징해서, 어디서나 동일한 환경에서 실행할 수 있다. 네트워크 구성이 복잡할 수 있다.
        • NCP(Naver Cloud Platform): 네이버에서 지원하는 클라우드 인프라. 서버, 데이터베이스, 네트워크 등 다양한 서비스 제공. 한국 최적화 + 네부캠 무료라는 큰 장점. AWS 같은 글로벌 클라우드 서비스에 비해서는 확장성과 생태계가 한정적이다.
        • GitHub Actions: 깃허브의 CI/CD 도구로, 코드 저장소에 변경 사항이 발생할 때마다 자동으로 빌드, 테스트, 배포 파이프라인을 실행할 수 있다. 설정해두면 자동화 + 깃허브 통합으로 편하다. 근데 설정이 복잡할 수 있고 오히려 깃 레포에 제한이라고 볼 수 있다.
    • ERD 어떻게 짜는지 알아오기
    • 아키텍쳐 어떤게 좋을지 알아오기
      • atomic design 패턴은 아키텍처는 아니고 디자인 패턴이지만 사용후기를 이야기 하자면, 나누는 기준에 주관이 많이 들어감.

      • FE: FSD

        image1.png
        • 기능별로 파일과 폴더 구조를 구성하는 방법으로, 컴포넌트의 역할별 분리와 특정 기능 단위로 모듈화

        • 6 Layer는 이름 고정

          • app 전체 컴포넌트.
          • pages 라우터에 따른 페이지들. 주소별 페이
          • widgets 레이아웃(안의 내부 내용은 바뀌지만, 전체 틀은 비슷한 것) / feature의 묶음
          • features 컴포넌트 폴더 (데이터를 뺀 모양)
          • entities 컴포넌트 폴더 (데이터 그 자체).
          • shared 공유하는 것(재사용할 수 있는 UI, utils, hooks, typings …). slices 없음
        • Slices

          • 최대한 별개의 것들로 나눔
        • Segments

          • ui는 화면, model은 데이터 담당, api는 서버로부터 데이터 가져오는 axios나 fetch같은 것. 기타 유틸들은 lib 폴더 안에 넣어두
        • 특징

          • 뎁스는 최대 3단계 (필요하다면 한 뎁스정도는 더 가능)
          • 위 layer들만 import
          • import는 무조건 index
        • 아직도 React 폴더 구조로 고민하고 계신가요? FSD 한 번 써보세요[제로초뉴스]

        • https://emewjin.github.io/feature-sliced-design/?utm_source=substack&utm_medium=email

        • Nuke App 🦄⚛️

      • BE: MVC

  • 홍현지

    • 정해진 기술 스택 장단점 조사해오기

      FE: React + Typescript + Vite

      웹 프레임워크

      React

      • 장점
        • csr방식이다 보니 동적인 페이지 개발 가능
        • 초기 설정이 간단함

      Next.js

      • 장점
        • ssr 방식 → seo 친화적
        • pre-rendering 방식으로 인해 웹페이지 미리보기 가능
        • 풀스택 개발 지원
      • 단점
        • react에 비해선 초기 설정이 무겁고 번들 사이즈가 큼.
        • React에 Vite만 사용했을 때와 비교 했을 때의 오버헤드 / 안정성 / 유지보수성에 단점 및 러닝 커브 이슈 존재

      React 선택 이유

      • seo 불필요: 워크스페이스 환경은 private 해야함.
      • Next.js의 장점을 활용할 필요성이 없는 프로젝트
        • 모든 핵심 기능이 워크스페이스에 몰려있으며, 변경사항에 대해 클라이언트 측에서 빠르게 반응해야하기에 정적인 웹사이트를 렌더링 하는 방식인 ssr과는 적합하지 않음.
        • 위와 같은 맥락으로 변경사항이 계속 생길 수 있는 동적인 성격의 웹이다보니, 서버에서 렌더링해줄 필요가 없음
      • 프론트와 백엔드의 경계를 명확하게 나눌 수 있다는 점
      • Next가 기능이 많은 만큼 사이즈나 초기 설정이 무거운 반면, react는 Next에 비해 가볍게 쓸 수 있다고 생각..
      • Next는 러닝커브가 존재 (팀원 중 반이 안써본 것 같은데) / 프레임워크를 익히는 것에서 기술적 도전을 챙겨가고 싶지 않다.

      CSS: tailwind css

      tailwindcss

      • 장점
        • zero configuration 방식으로, 설정 파일을 수정하거나 복잡한 초기 설정을 건드리지 않고도 바로 사용할 수 있다.
        • rust 엔진으로 동작하기 때문에 성능이 좋다.
        • 패키지가 다른 스타일 라이브러리들에 비해 가볍다
        • 생산성이 좋다.
      • 단점
        • 동적 스타일 적용에 대한 유연성이 떨어짐
        • 가독성이 좋지 않다.

      styled-component

      • 장점
        • css 스타일 자체에서의 동적 스타일 적용이 용이
        • 컴포넌트 스타일 모듈화..? 컴포넌트를 아예 스타일 적용해서 만들어준다..?
        • 가독성이 좋다.
      • 단점
        • css-in-js 방식으로 런타임에서 스타일을 생성하기 때문에 런타임 성능 부담 + 캐싱 전략이 어려움으로 인해 성능 저하가 있음.
        • js를 객체로 관리해 번들 크기가 커질 수 있다.
        • 컴포넌트 이름 정하기 어렵다.

      상태 관리: Zustand + Tanstack-Query

      zustand

      • 장점
        • 번들 사이즈가 작다.
        • 사용법을 익히고 활용하기 어렵지 않다.
        • 소~중 규모의 상태관리에 적합하다.

      BE: Express + MongoDB + Mongoose

      백엔드 프레임워크

      Express

      • 장점
        • 빠르게 백엔드를 구축하고 확장할 수 있다.
        • 프레임워크가 가볍기 때문에 간단한 서버 성능만을 고려한다면 적합하다.
        • 설정에 대한 자유도가 높다.
      • 단점
        • 자유도가 높은 만큼 초기 세팅과 컨벤션 맞추기가 어렵다.
        • 타입스크립트 지원이 없다 (별도 세팅을 해야한다.)

      NestJs

      • 장점
        • 타입스크립트가 기본적이다보니 초기설정의 부담감이 적다.
        • HTTP 연결, DB 연동, 미들웨어 구축, 인증 보안 등 RESTful api 서비스를 제작하기에 필요한 기능들을 기본적으로 제공하고 있다.
        • 필요한 클래스를 일괄적으로 자동생성해주는 명령어 제공
        • 스웨거 문서를 자동으로 달 수 있다.
      • 단점
        • 백엔드에 익숙치 않은 편이라면 러닝커브가 존재한다.
        • 다양한 기본 설정들이 존재하기 때문에 프레임워크가 무거워 간단한 서버 성능을 고려한다면 사용이 비효율적이다.
        • 자유도가 낮다.

      Express 선택 이유

      • NestJS는 러닝커브가 크다.
      • NestJS의 장점을 활용할 필요성이 없는 프로젝트
        • 우선순위로 따져보았을 때, 현재 프로젝트에선 사용자 인증이 크게 중요하지 않다.
        • 많은 api가 필요하지 않다.
        • 후순위 기능들 개발도 진행하게 된다 해도 백엔드의 볼륨이 크게 커지지 않는다.

      데이터베이스

      MongoDB

      • 장점
        • JSON 형식의 document 기반 구조이므로 스키마 사전 정의 필요가 없으며, 비정형 데이터를 저장하기에 알맞다.
        • 쿼리 기능이 풍부하다.

      배포: nginx + docker + ncp + github actions

      nginx

      • 장점
        • ssl/tls 지원 → https 설정 가능
        • 단일 서버에서 여러 어플을 같이 올릴 수 있음 (프론트/백 서버 분리 필요x)
        • 다양한 프록시 기능 제공하여 백엔드와의 통신 최적화 가능
          • 정적파일들을 잘 캐싱하고, 빠르게 제공 가능
          • 로드 밸런싱 제공

      docker

      • 장점
        • 빠른 배포 가능 → 새로운 버전 업데이트에 용이
        • 컨테이너 가상환경
          • 호스트 서버에 영향 x
          • 가상화 경량화
        • 커뮤니티 활성화 → docker 이미지를 배포하려는 환경설정에 맞춰 가져와서 활용하기가 용이함
    • 백엔드 구조+아키텍쳐

      asdf.png

      참고하면 좋을 아키텍처

      • good
      image.png d02489e8_1.png
      • bad
      image.png image.png

      서버 아키텍처를 어떻게 세워야할까

      • xml, html코드, css코드를 아예 파일형태로 해서 스토리지에 저장할까 고민됨

        • 스토리지에 저장하면 수정사항이 생길때마다 해당 파일을 삭제했다가 다시 생성해줘야하는데 이럼 서버 부담과 비용이 너무 커질 것 같아서..

        → 엔트리는 어떻게 저장되는지 찾아봄

      • 파일 형태를 3개로 나눈 이유
      • 어차피 클라이언트 측에서 블록 수정이 이루어질때마다 실시간 프리뷰 반영을 해줘야하니 xml 코드와 더불어 파싱된 html, css 코드를 계속 들고 있을 것이니 이걸 그대로 저장해줘도 되겠다 생각함.
        • xml 코드만 저장해줄 경우

          → 워크스페이스 불러와서 화면 렌더링 할 때 시간이 오래 소요될 것이라고 예상

          → xml 코드에서 html코드와 css 코드를 전부 다시 파싱해줘야하니

        • 3개를 분리해서 저장해줄 경우

          → 워크스페이스 정보 불러와서 화면 렌더링할 때 이미 html, css 코드도 받아오니 렌더링 되는 시간이 줄 것이라고 예상.

      엔트리는 어떻게 정보를 저장하고 있는가

      • graphQL 사용함 (요청 보내는 json의 프로퍼티가 너무 많아서 그런 것 같음)

      프로젝트 생성

      • 기본 템플릿은 클라이언트 측에서 정적인 파일로 가져와서 렌더링 하는 것 같음 (클라이언트 역할)

      • 작품 만들기 하자마자 db에 create 요청보내는 게 아니라, 임시로 작품 만들기 페이지를 먼저 보여주는 것 같음

        • 저장하기 버튼을 눌러야만 새로운 프로젝트 생성 요청을 보냄
          • return은 프로젝트 id값으로 돌아옴
        • 프리뷰 캔버스창 스크린샷을 찍고, 프로젝트 id값을 경로에 넣어서 어디론가 post 요청을 보내는 것은 확실한테 스토리지가 맞는지 아닌지 모르겠음..
          • 아마 사진을 비트화 시키고 암호화 시켜서 보낸 건 아닐까?
            • 요청 헤더의 타입이 json타입이고, 암호화된 것 같은 문자열이 value로 들어감
          • 백엔드에서 비트화 시킨 사진을 다시 복구하고 그걸 스토리지에 저장한 후 스토리지 경로를 응답으로 던져주는 것 같음
      • 요청 보낼때 variables

        • 전체

          {
              "category": "기타",
              "scenes": [
                  {
                      "id": "7dwq",
                      "name": "장면 1"
                  }
              ],
              "variables": [
                  {
                      "name": "초시계",
                      "id": "brih",
                      "visible": false,
                      "value": "0",
                      "variableType": "timer",
                      "isCloud": false,
                      "isRealTime": false,
                      "cloudDate": false,
                      "object": null,
                      "x": 134,
                      "y": -70
                  },
                  {
                      "name": "대답",
                      "id": "1vu8",
                      "visible": false,
                      "value": "0",
                      "variableType": "answer",
                      "isCloud": false,
                      "isRealTime": false,
                      "cloudDate": false,
                      "object": null,
                      "x": 150,
                      "y": -100
                  }
              ],
              "objects": [
                  {
                      "id": "7y0y",
                      "name": "엔트리봇",
                      "script": "[[{\\"id\\":\\"dwqz\\",\\"x\\":50,\\"y\\":30,\\"type\\":\\"when_run_button_click\\",\\"params\\":[null],\\"statements\\":[],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]},{\\"id\\":\\"c31v\\",\\"x\\":0,\\"y\\":0,\\"type\\":\\"repeat_basic\\",\\"params\\":[{\\"id\\":\\"brmv\\",\\"x\\":0,\\"y\\":0,\\"type\\":\\"number\\",\\"params\\":[10],\\"statements\\":[],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]},null],\\"statements\\":[[{\\"id\\":\\"1rv5\\",\\"x\\":0,\\"y\\":0,\\"type\\":\\"move_direction\\",\\"params\\":[{\\"id\\":\\"61zx\\",\\"x\\":0,\\"y\\":0,\\"type\\":\\"number\\",\\"params\\":[10],\\"statements\\":[],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]},null],\\"statements\\":[],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]}]],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]}]]",
                      "objectType": "sprite",
                      "rotateMethod": "free",
                      "scene": "7dwq",
                      "sprite": {
                          "pictures": [
                              {
                                  "id": "vx80",
                                  "dimension": {
                                      "width": 144,
                                      "height": 246
                                  },
                                  "fileurl": "/lib/entry-js/images/media/entrybot1.svg",
                                  "thumbUrl": "/lib/entry-js/images/media/entrybot1.svg",
                                  "name": "엔트리봇_걷기1",
                                  "imageType": "svg"
                              },
                              {
                                  "id": "4t48",
                                  "dimension": {
                                      "width": 144,
                                      "height": 246
                                  },
                                  "fileurl": "/lib/entry-js/images/media/entrybot2.svg",
                                  "thumbUrl": "/lib/entry-js/images/media/entrybot2.svg",
                                  "name": "엔트리봇_걷기2",
                                  "imageType": "svg"
                              }
                          ],
                          "sounds": [
                              {
                                  "duration": 1.3,
                                  "ext": ".mp3",
                                  "id": "8el5",
                                  "fileurl": "/lib/entry-js/images/media/bark.mp3",
                                  "name": "강아지 짖는 소리"
                              }
                          ]
                      },
                      "selectedPictureId": "vx80",
                      "lock": false,
                      "entity": {
                          "x": 0,
                          "y": 0,
                          "regX": 72,
                          "regY": 123,
                          "scaleX": 0.5128205128205128,
                          "scaleY": 0.5128205128205128,
                          "rotation": 0,
                          "direction": 90,
                          "width": 144,
                          "height": 246,
                          "font": "undefinedpx ",
                          "visible": true
                      }
                  }
              ],
              "expansionBlocks": [],
              "aiUtilizeBlocks": [],
              "speed": 60,
              "name": "241031_Honghong_t 작품",
              "likeCnt": 0,
              "visit": 0,
              "isopen": false,
              "user": "6591921bc62948002505ea4c",
              "messages": [],
              "functions": [],
              "tables": [],
              "interface": {
                  "canvasWidth": 324,
                  "object": "7y0y"
              },
              "hardwareLiteBlocks": [],
              "externalModules": [],
              "externalModulesLite": [],
              "isPracticalCourse": false,
              "blockLog": {
                  "categories": [
                      "event",
                      "repeat",
                      "walk"
                  ],
                  "when_run_button_click": 1,
                  "repeat_basic": 1,
                  "number": 2,
                  "move_direction": 1
              }
          }
          
        • 중요한 부분

          • 엔트리는 프리뷰에 있는 오브젝트 요소마다 블록을 조립해줌
            • 조립된 블록 정보가 script로 들어가는 것 같음
          • 블록 정보에 대한 json객체가 문자열로 변경되고 script 속성에 담겨 넘겨지는 것 같음
          objects: [
              {
                  "id": "7y0y",
                  "name": "엔트리봇",
                  **"script"**: "[[{\\"id\\":\\"dwqz\\",\\"x\\":50,\\"y\\":30,\\"type\\":\\"when_run_button_click\\",\\"params\\":[null],\\"statements\\":[],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]},{\\"id\\":\\"c31v\\",\\"x\\":0,\\"y\\":0,\\"type\\":\\"repeat_basic\\",\\"params\\":[{\\"id\\":\\"brmv\\",\\"x\\":0,\\"y\\":0,\\"type\\":\\"number\\",\\"params\\":[10],\\"statements\\":[],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]},null],\\"statements\\":[[{\\"id\\":\\"1rv5\\",\\"x\\":0,\\"y\\":0,\\"type\\":\\"move_direction\\",\\"params\\":[{\\"id\\":\\"61zx\\",\\"x\\":0,\\"y\\":0,\\"type\\":\\"number\\",\\"params\\":[10],\\"statements\\":[],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]},null],\\"statements\\":[],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]}]],\\"movable\\":null,\\"deletable\\":1,\\"emphasized\\":false,\\"readOnly\\":null,\\"copyable\\":true,\\"assemble\\":true,\\"extensions\\":[]}]]",
                  "objectType": "sprite",
                  "rotateMethod": "free",
                  "scene": "7dwq",
                  "sprite": {
                      "pictures": [
                          {
                              "id": "vx80",
                              "dimension": {
                                  "width": 144,
                                  "height": 246
                              },
                              "fileurl": "/lib/entry-js/images/media/entrybot1.svg",
                              "thumbUrl": "/lib/entry-js/images/media/entrybot1.svg",
                              "name": "엔트리봇_걷기1",
                              "imageType": "svg"
                          },
                          {
                              "id": "4t48",
                              "dimension": {
                                  "width": 144,
                                  "height": 246
                              },
                              "fileurl": "/lib/entry-js/images/media/entrybot2.svg",
                              "thumbUrl": "/lib/entry-js/images/media/entrybot2.svg",
                              "name": "엔트리봇_걷기2",
                              "imageType": "svg"
                          }
                      ],
                      "sounds": [
                          {
                              "duration": 1.3,
                              "ext": ".mp3",
                              "id": "8el5",
                              "fileurl": "/lib/entry-js/images/media/bark.mp3",
                              "name": "강아지 짖는 소리"
                          }
                      ]
                  },
                  "selectedPictureId": "vx80",
                  "lock": false,
                  "entity": {
                      "x": 0,
                      "y": 0,
                      "regX": 72,
                      "regY": 123,
                      "scaleX": 0.5128205128205128,
                      "scaleY": 0.5128205128205128,
                      "rotation": 0,
                      "direction": 90,
                      "width": 144,
                      "height": 246,
                      "font": "undefinedpx ",
                      "visible": true
                  }
              }
          ]
          
      • 요청 보내는 json객체를 보아하니, 무조건 nosql

      ⇒ 결론적으로 엔트리는 스크립트 정보를 string으로 변경해서 json 객체에 담아 요청 보냈던 걸로..

      • 다만 우리 프로젝트는 저장해야하는 큰 스크립트정보가 3개인 것 같은데 이걸 어떻게 해야하나?
        1. 엔트리랑 똑같이 3개 정보도 그냥 문자열로 변경해서 json 객체에 담아 요청 보내고 그대로 mongodb에 저장하는 방식으로
        2. xml코드만 문자열로 변경 후 script속성값으로 담고 + css 정보는 json 객체로 만들어서 요청
        3. 3개의 파일 전부 스토리지에 저장

      → 개인적인 의견으로는 2번 방법이 베스트일 것 같음.

      클라이언트측에선 작품 크기가 커질수록 렌더링 시간 오래걸림 이슈가 있겠지만,

      추후 동시편집까지의 확장성을 고려한다면, 스토리지는 쓰지 않아야한다고 생각

      → 동시편집+스토리지 ? 서버 비용이 정말 커질 것 같다.

회의

스키마

🏠 Home

🔍 세부 기능

🚀 프로젝트

⭐ 팀 목표

🤝 협업

📖 BooLock위키

J018_권나연
J189_이영재
J190_이유진
J252_최경일
J281_홍현지

🎙️ 발표

💡 회고

🗣️ 회의록

0️⃣주차
1️⃣주차
2️⃣주차
3️⃣주차
4️⃣주차
5️⃣주차

📷 갤러리

Clone this wiki locally