-
Notifications
You must be signed in to change notification settings - Fork 2
10월 31일 회의
# 숙제 검사
- TMI 한 가지씩 준비 (구두)
- 정해진 기술 스택 장단점 조사해오기
- ERD 어떻게 짜는지 알아오기
- 아키텍쳐 어떤게 좋을지 알아오기
# 10.31(목)
- 발표 준비(1시반 ~ 3시 반까지)
- 멘토링 일지 작성(4시 ~ 6시)
- 아키텍처
- 데이터 저장 방식
- 디자인 마무리
- 파트 나누기
- 초기 세팅
- 라이브러리 설치
- 깃허브
- 기술 선택 기술적 근거 정리
-
권나연
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 # 프로젝트 설명 문서
홈
- 학습 컨텐츠 데이터
- 특정 회원의 학습 컨텐츠 진행도
- 특정 회원의 워크스페이스
작업
- 블록 조립 상태 → 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/
-
이영재
-
정해진 기술 스택 장단점 조사해오기
장점
- 공식 문서가 아주 자세히 작성되어 있다.
- 방대한 커뮤니티, 자료를 통해 쉽게 접하고 배울 수 있음
- 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를 사용하면 극복할 수 있음
- 상태 관리가 복잡함
- 컴포넌트 기반 구조에서 여러 컴포넌트 간의 상태를 관리하는 것은 많이 복잡함
- 다양한 상태 관리 라이브러리가 있지만 이를 잘 다루기 위해서는 경험을 많이 쌓아야함
장점
- 빠른 개발 서버
- ESM을 기반으로 서버를 구동하여, 초기 로딩이 빠름
- HMR (Hot Module Replacement)
- Vite는 변경된 모듈만 갱신하기 때문에 페이지 전체를 다시 로드할 필요가 없으며, 변경 사항을 실시간으로 반영하여 개발 생산성을 높힘
- 간편한 설정과 플러그인 시스템
- Vite는 기본 설정이 간단함
- 플러그인 시스템을 통해 쉽게 기능을 확장할 수 있음
- 번들링과 빌드 속도가 빠름
- vite는 빌드 단계에서 Rollup을 사용하여 최적화된 번들링을 수행함
- 코드 스플리팅, 트리 셰이킹, 압축 등의 작업이 Rollup으로 최적화되어, 배포 환경 빌드도 매우 빠르게 수행할 수 있음
- 다양한 프레임워크와 호환됨
- Vite는 React, Vue, Svelte 등 여러 프레임워크와 호환이 됨
단점
- 레거시 브라우저 지원에 한계가 있음
- Vite는 최신 ESM을 기반으로 하기에, IE11 같은 레거시(구형) 브라우저에서는 호환성 문제가 있을 수 있음
- 플러그인 호환성 문제
- Vite는 Webpack에 비하면 최신 번들러이기 때문에, Webpack에서 사용하는 특정 플러그인이나 로더가 Vite와 호환되지 않을 수 있음
- Webpack에 비해 생태계가 작음
- 비교적 최신에 나온 번들러이기 때문에 역사가 깊은 Webpack에 비해 커뮤니티가 작고 자료가 적음
- 대규모 프로젝트 지원에 한계가 있을 수 있음
- Vite는 대규모 프로젝트에서도 사용 가능하지만 파일 의존성이 매우 복잡한 경우 빌드 속도가 상대적으로 느려질 수 있음
장점
-
자바스크립트의 슈퍼셋
- 기존 자바스크립트와 완벽하게 호환됨
- 기존 자바스크립트 프로젝트에 타입스크립트를 점진적으로 적용해서 마이그레이션할 수 있음
- Ts와 Js를 혼용해도 문제가 없음
-
컴파일 언어 + 정적 언어임
- 컴파일 과정에서 오류를 찾을 수 있음
- TS는 타입을 명시하여 개발 단계에서의 안정성을 높여줌
- 타입을 미리 체크하기에 JS에 비해 실행 시간이 빠름
-
객체 지향 가능
- 자바스크립트로도 객체 지향 프로그래밍이 가능함. 하지만 타입스크립트는 class에서 private 기능 지원, interface, type 등을 통해 더 쉽게 객체 지향 프로그래밍을 할 수 있음
단점
-
러닝커브
- 자바스크립트에 익숙해져 있으면 타입을 익히고 적절하게 지정하는데 시간이 걸림
- interface, type, enum 등 자바스크립트에서는 없는 기능이 추가됨
-
가독성 저하
-
기존 자바스크립트 코드에 타입을 선언하고 지정하는 과정에서 코드의 수가 증가함
→ 이걸 부정적으로 보는 개발자들이 있음
-
-
any 남발
-
type에 익숙치 않으면 any를 남발하는데 이는 타입스크립트의 장점인 타입 지정을 완전 무시하고 쓰는 꼴임
→ 안쓰는 것보다 못하는 꼴이 되어버림
-
장점
- 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를 적용할 수 있음
- tailwind.config.js를 설정하거나
- 반응형 디자인 구현이 쉬움
- 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는 설정이 매우 간단하고, 상태를 생성하고 사용할 수 있는 API가 직관적임
-
create
함수로 상태를 정의하고 컴포넌트에서 바로 상태를 가져와 사용할 수 있어 코드가 깔끔함 -
동작을 이해하기 위해 알아야 하는 코드 양이 매우 적음
- 핵심 로직의 코드 줄 수가 약 42줄 밖에 되지 않음
-
-
리액트와 호환이 잘됨
- React의 context 시스템에 의존하지 않아서 불필요한 리렌더링을 줄임. 상태를 구독한 컴포넌트만 리렌더링되어 성능 최적화가 용이
-
보일러 플레이트가 거의 없음
- context api와 비교해보면 확실함
-
typescript 지원 !
단점
- 중대형 애플리케이션에 비해 제한된 기능
- zustand는 간결함을 목표로 설계되었기에 Redux 같은 복잡한 상태 관리 라이브러리에서 지원하는 복잡한 상태 관리나 미들웨어 지원이 제한적임
- 정식 DevTools 미지원
- 기본적으로 Redux DevTools와 같은 공식 디버깅 도구를 지원하지 않아서 상태 변화를 추적하는 데 어려움이 있을 수 있음
장점
-
데이터 페칭과 캐싱 최적화
-
React Query는 데이터를 자동으로 캐싱하여, 같은 데이터를 여러 번 요청하지 않고 캐시에서 가져올 수 있도록 함
이를 통해 불필요한 네트워크 요청을 줄이고 성능을 최적화할 수 있음
-
-
자동 리페치
- 데이터가 오래 되었거나 백그라운드에서 변경되면 자동으로 최신 데이터를 가져옴
- 자동으로 데이터를 동기화해서 최신 상태를 유지할 수 있음
-
페이징 및 무한 스크롤 지원
- React query는 페이징과 무한 스크롤을 기본으로 제공하여, 많은 데이터가 필요할 때 유용함
- 추가적인 설정이나 로직 구현이 불필요
-
Typescript와 호환이 잘됨
-
서버 상태 관리 가능
- 데이터 동기화, 캐싱, 자동 리페치, 캐시 무효화 등의 복잡한 서버 상태 관리를 제공함
단점
- 러닝 커브
- React Query의 기능은 많고 설정할 수 있는 옵션이 다양해서 React Query를 처음 접한 사람들을 익히는데 시간이 많이 걸림
- 클라이언트 상태 관리 부재
- React Query는 서버 상태 관리에 최적화되어 있지만, 클라이언트 상태 관리에는 적합하지 않음
- 캐시 메모리 관리 필요
- 데이터를 캐싱하지만 오래된 캐시 데이터가 메모리에 남아 있을 수 있음
- 동일한 데이터를 여러 컴포넌트에서 관리할 때, 각 컴포넌트가 다른 쿼리 키를 사용할 경우 데이터 일관성 문제가 발생할 수 있음
장점
- 경량 프레임워크
- Express는 기본 기능만을 제공함으로 가볍고 유연함
- 필요에 따라 다양한 미들웨어와 모듈을 추가해 맞춤형 서버를 구축할 수 있음
- 간단하고 직관적인 API
- Express는 HTTP 요청 및 응답을 다루기 쉽게 설계되어 있음
- 또한 API가 간결하고 직관적임
- 커뮤니티가 크고 다양한 라이브러리를 지원
- Express는 nodejs 기반으로 오랫동안 사용된 프레임워크이기 때문에 많은 자료와 라이브러리가 존재함
- 미들웨어를 통한 기능 확장
- 미들웨어를 통해 인증, 데이터 유효성 검사, 로깅, 에러 핸들링 등 다양한 기능을 간단히 추가할 수 있음
- 풀스택 개발 환경 지원
- 자바스크립트 기반이기 때문에 풀스택으로 개발할 수 있음
단점
- 경량 프레임워크
- 너무 유연해서 프로젝트 구조를 설계하고 아키텍처를 적용하는데 많은 시간이 걸림
- 미들웨어 사용으로 인한 복잡성 증가
- 기능 확장을 위해 미들웨어를 추가하는 것이 장점이기도 하지만, 많은 미들웨어를 사용하면 요청을 처리하는 로직이 복잡해지고 성능이 저하될 수 있음
- 실시간 기능에 대한 한계
- Express는 웹 소켓을 지원하지 않음
- 실시간 데이터 처리가 필요한 경우 Socket.IO와 같은 별도의 라이브러리를 도입해야됨
장점
-
유연한 스키마 구조
- MongoDB는 스키마가 고정되지 않아 데이터 구조를 유연하게 변경할 수 있음
- 관계형 데이터베이스와 달리 새로운 필드나 데이터를 추가할 때 미리 정의할 필요가 없음
-
높은 확장성
- MongoDB는 수평 확장을 지원하여 데이터가 많아지면 서버를 쉽게 추가할 수 있음
💡수평 확장? 서버의 자체적인 스펙을 업그레이드 하는 것이 아닌 서버의 개수를 늘리는 방법
-
문서 지향 데이터 모델
- JSON과 유사한 BSON (Binary JSON) 형식으로 데이터를 저장하므로, JavaScript 및 Node.js 환경과 호환이 잘 됨
단점
- 데이터 일관성 문제
- MongoDB는 기본적으로 최종적 일관성 모델을 채택하고 있기에, 데이터가 모든 복제본에 즉시 동기화 되지 않을 수 있음
- 복잡한 조인 연산의 한계
- MongoDB는 SQL의 JOIN과 같은 복잡한 조인 연산을 기본적으로 지원하지 않아, 여러 컬렉션에 걸친 데이터를 다룰 때 불편할 수 있음
-
ERD 어떻게 짜는지 알아오기
-
아키텍쳐 어떤게 좋을지 알아오기
- 프론트엔드
-
FSD 아키텍처
(번역) 기능 분할 설계 - 최고의 프런트엔드 아키텍처
-
App/Pages/Widgets/Features/Entities/Shared의 계층 구조
-
관심사 분리가 명확함
- 컴포넌트 별로 폴더를 나눌 수 있음
-
신생 패턴이지만 공식 문서에서 fsd 패턴을 적용한 다양한 패턴을 확인할 수 있음
-
-
atomic design
- 지난 뉴스스탠드에서 atomic design을 적용하신 분의 코드를 보았는데 그 분이 말하길 atomic 디자인은 프로젝트의 설계가 매우 중요하고 atom 같이 작은 단위의 컴포넌트의 설계를 조금만 바꿔도 side effect가 너무 크다고 별로 추천하지 않는다는 말을 듣고 별로 하고 싶지 않아졌..습니다..
-
- 백엔드
-
MVC 패턴
- 가장 대중적이고 가장 많이 사용해서 그나마 러닝 커브가 낮지 않을까 싶습니다..
- Model-View-Controller에서 Model이랑 Controller만 사용하고 Service, Repository는 필요성을 느낄 때 추가하는 걸로 하면 좋을 것 같습니다.
- 관심사 분리가 명확해서 코드 분리하기가 좋은 것 같습니다.
-
- 프론트엔드
-
-
이유진
생각해보니까 저희 사용할 라이브러리도 총 정리해도 좋을듯. 폴더 구조 잡을 때 고려해야해서!
- 장점: 프론트 프레임워크 중 1등. 커뮤니티가 잘 형성되어 있음.
- 단점: -
- 장점: 타입 안정성을 제공해서 코드의 오류를 줄인다. 유지 보수 및 협업에 유리하다.
- 단점: 컴파일 시간이 생긴다.
- 장점: 설정이 매우 간편하다. HMR로 빌드 속도와 개발 환경이 빠르다. ESM을 사용해서 최신 브라우저에 적합하다.
- 단점: 구형 브라우저 지원에 제약이 있고 webpack에 비해 플러그인이 적다.
- 장점: 유틸리티 기반으로 빠르게 스타일링을 할 수 있고 디자인의 일관성을 유지하기 좋다.
- 단점: 코드의 가독성이 떨어지고 비표준 스타일링을 적용할 때 제약이 있을 수 있다.
- 장점: 러닝커브가 적다.
- 단점: redux에 비해 미들웨어 지원에서 부족하다. 커뮤니티가 적다.
- 장점: 비동기 데이터 관리에 특화되어 있고, 데이터 캐싱과 최신성 유지가 자동으로 된다.
- 단점: 데이터 캐싱 전략이 복잡할 수 있다.
- 장점: 커뮤니티가 활발하고 경량 서버라 node.js와 잘 결합되어 빠른 응답을 받을 수 있다.
- 단점: 대규모 프로젝트에서는 관리가 어려울 수 있다.
- 장점: 유연한 스키마를 저장할 수 있고 json 형식으로 데이터를 저장해 javascript와 연동하기 쉽다. 확장성이 좋다.
- 단점: 데이터 관계가 복잡하면 스키마를 나누기 어려울 수 있다.
-
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
- layers → slice(도메인) → segments
- 폴더의 depth가 3단계로 제한됨
- 위에서 아래로 import는 되지만 아래에서 위로 import는 안됨 → 위계질서가 생김
- 일단 어려움. 그리고 적응 기간이 있음. 초기에는 오래 걸릴 수 있음. 전에 써본 분이 욕한 기억이… 팀원 모두가 규칙을 잘 지켜야 함. 안그러면 의미가 없는 아키텍처
- processes는 사라짐
- 구조
-
app(전역 컴포넌트 app.tsx)
-
page
-
widgets(component - 재사용 기준)
-
features(기능)
-
entities(데이터)
-
shared(utiles, hooks, …) → slices가 없음
-
https://www.youtube.com/watch?v=64Fx5Y1gEOA
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 환경 변수 타입 정의
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 도구로, 코드 저장소에 변경 사항이 발생할 때마다 자동으로 빌드, 테스트, 배포 파이프라인을 실행할 수 있다. 설정해두면 자동화 + 깃허브 통합으로 편하다. 근데 설정이 복잡할 수 있고 오히려 깃 레포에 제한이라고 볼 수 있다.
- FE:
- ERD 어떻게 짜는지 알아오기
- 아키텍쳐 어떤게 좋을지 알아오기
-
atomic design 패턴은 아키텍처는 아니고 디자인 패턴이지만 사용후기를 이야기 하자면, 나누는 기준에 주관이 많이 들어감.
-
FE: FSD
-
기능별로 파일과 폴더 구조를 구성하는 방법으로, 컴포넌트의 역할별 분리와 특정 기능 단위로 모듈화
-
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
-
https://emewjin.github.io/feature-sliced-design/?utm_source=substack&utm_medium=email
-
-
BE: MVC
-
- 정해진 기술 스택 장단점 조사해오기
-
홍현지
-
정해진 기술 스택 장단점 조사해오기
- 장점
- csr방식이다 보니 동적인 페이지 개발 가능
- 초기 설정이 간단함
- 장점
- ssr 방식 → seo 친화적
- pre-rendering 방식으로 인해 웹페이지 미리보기 가능
- 풀스택 개발 지원
- 단점
- react에 비해선 초기 설정이 무겁고 번들 사이즈가 큼.
- React에 Vite만 사용했을 때와 비교 했을 때의 오버헤드 / 안정성 / 유지보수성에 단점 및 러닝 커브 이슈 존재
- seo 불필요: 워크스페이스 환경은 private 해야함.
- Next.js의 장점을 활용할 필요성이 없는 프로젝트
- 모든 핵심 기능이 워크스페이스에 몰려있으며, 변경사항에 대해 클라이언트 측에서 빠르게 반응해야하기에 정적인 웹사이트를 렌더링 하는 방식인 ssr과는 적합하지 않음.
- 위와 같은 맥락으로 변경사항이 계속 생길 수 있는 동적인 성격의 웹이다보니, 서버에서 렌더링해줄 필요가 없음
- 프론트와 백엔드의 경계를 명확하게 나눌 수 있다는 점
- Next가 기능이 많은 만큼 사이즈나 초기 설정이 무거운 반면, react는 Next에 비해 가볍게 쓸 수 있다고 생각..
- Next는 러닝커브가 존재 (팀원 중 반이 안써본 것 같은데) / 프레임워크를 익히는 것에서 기술적 도전을 챙겨가고 싶지 않다.
- 장점
- zero configuration 방식으로, 설정 파일을 수정하거나 복잡한 초기 설정을 건드리지 않고도 바로 사용할 수 있다.
- rust 엔진으로 동작하기 때문에 성능이 좋다.
- 패키지가 다른 스타일 라이브러리들에 비해 가볍다
- 생산성이 좋다.
- 단점
- 동적 스타일 적용에 대한 유연성이 떨어짐
- 가독성이 좋지 않다.
- 장점
- css 스타일 자체에서의 동적 스타일 적용이 용이
- 컴포넌트 스타일 모듈화..? 컴포넌트를 아예 스타일 적용해서 만들어준다..?
- 가독성이 좋다.
- 단점
- css-in-js 방식으로 런타임에서 스타일을 생성하기 때문에 런타임 성능 부담 + 캐싱 전략이 어려움으로 인해 성능 저하가 있음.
- js를 객체로 관리해 번들 크기가 커질 수 있다.
- 컴포넌트 이름 정하기 어렵다.
- 장점
- 번들 사이즈가 작다.
- 사용법을 익히고 활용하기 어렵지 않다.
- 소~중 규모의 상태관리에 적합하다.
- 장점
- 빠르게 백엔드를 구축하고 확장할 수 있다.
- 프레임워크가 가볍기 때문에 간단한 서버 성능만을 고려한다면 적합하다.
- 설정에 대한 자유도가 높다.
- 단점
- 자유도가 높은 만큼 초기 세팅과 컨벤션 맞추기가 어렵다.
- 타입스크립트 지원이 없다 (별도 세팅을 해야한다.)
- 장점
- 타입스크립트가 기본적이다보니 초기설정의 부담감이 적다.
- HTTP 연결, DB 연동, 미들웨어 구축, 인증 보안 등 RESTful api 서비스를 제작하기에 필요한 기능들을 기본적으로 제공하고 있다.
- 필요한 클래스를 일괄적으로 자동생성해주는 명령어 제공
- 스웨거 문서를 자동으로 달 수 있다.
- 단점
- 백엔드에 익숙치 않은 편이라면 러닝커브가 존재한다.
- 다양한 기본 설정들이 존재하기 때문에 프레임워크가 무거워 간단한 서버 성능을 고려한다면 사용이 비효율적이다.
- 자유도가 낮다.
- NestJS는 러닝커브가 크다.
- NestJS의 장점을 활용할 필요성이 없는 프로젝트
- 우선순위로 따져보았을 때, 현재 프로젝트에선 사용자 인증이 크게 중요하지 않다.
- 많은 api가 필요하지 않다.
- 후순위 기능들 개발도 진행하게 된다 해도 백엔드의 볼륨이 크게 커지지 않는다.
- 장점
- JSON 형식의 document 기반 구조이므로 스키마 사전 정의 필요가 없으며, 비정형 데이터를 저장하기에 알맞다.
- 쿼리 기능이 풍부하다.
- 장점
- ssl/tls 지원 → https 설정 가능
- 단일 서버에서 여러 어플을 같이 올릴 수 있음 (프론트/백 서버 분리 필요x)
- 다양한 프록시 기능 제공하여 백엔드와의 통신 최적화 가능
- 정적파일들을 잘 캐싱하고, 빠르게 제공 가능
- 로드 밸런싱 제공
- 장점
- 빠른 배포 가능 → 새로운 버전 업데이트에 용이
- 컨테이너 가상환경
- 호스트 서버에 영향 x
- 가상화 경량화
- 커뮤니티 활성화 → docker 이미지를 배포하려는 환경설정에 맞춰 가져와서 활용하기가 용이함
- 장점
-
백엔드 구조+아키텍쳐
- good
- bad
-
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개인 것 같은데 이걸 어떻게 해야하나?
- 엔트리랑 똑같이 3개 정보도 그냥 문자열로 변경해서 json 객체에 담아 요청 보내고 그대로 mongodb에 저장하는 방식으로
- xml코드만 문자열로 변경 후 script속성값으로 담고 + css 정보는 json 객체로 만들어서 요청
- 3개의 파일 전부 스토리지에 저장
→ 개인적인 의견으로는 2번 방법이 베스트일 것 같음.
클라이언트측에선 작품 크기가 커질수록 렌더링 시간 오래걸림 이슈가 있겠지만,
추후 동시편집까지의 확장성을 고려한다면, 스토리지는 쓰지 않아야한다고 생각
→ 동시편집+스토리지 ? 서버 비용이 정말 커질 것 같다.
-
- ✏️ 팀 목표
- ⛳ 그라운드 룰
- 🌳 Git 전략
- ✍️ Issue, PR 템플릿
- 🔒 커밋 컨벤션
- 🔒 FE/BE 코드 컨벤션