Skip to content

Commit

Permalink
[release] dev -> main 브랜치 merge / 1.0.1version upgrade (#24)
Browse files Browse the repository at this point in the history
* 🔨 refactor: Docker 이미지 최적화를 위한 코드 리팩토링

* 🙀 chore: 오타 수정

* 🐛 fix: COPY 경로 수정

* 🐛 fix: swagger-auto 명령어 추가

* 🙀 chore: 오타수정

* 🐛 fix: backend 이미지가 base-image를 사용하지 않으므로 새로운 패키지 매니저 설치 및 추가 파일 복사하는 로직 추가

* 🐛 fix: pnpm-lock 복사 코드 추가

* 🐛 fix: 서버 패키지 설치 안되는 오류 해결하기 위해 코드 수정

* 🐛 fix: eslint 경로 체크용 package 폴더 추가 복사

* 🐛 fix: 의존성을 복사해오고 dev용을 지우는 방식으로 수정

* 🐛 fix: pnpm-workspace 파일 복사

* 🙀 chore: cicd 스크립트 오타 수정

* 🙀 chore: copy 명령어 합침

* 🙀 chore: 코드 구조 변경

* 🐛 fix: COPY 경로 수정

* 🙀 chore: 경로 오타 수정

* 🔨 refactor: 배포시 swagger-auto 안 하게 로직 수정 및 prod 종속성에 필요한 패키지 dev 종속성에서 이동

* 🙀 chore: 경로 오타 수정

* 🙀 chore: 안쓰는 라이브러리 삭제

* 🚨 !HOTFIX!: env 설정 누락된 코드 추가

* 🚨 !HOTFIX!: env 설정 누락된 코드 추가

* [#10] 홈페이지 가상스크롤 적용 (#14)

* ✨ feat: scroll의 Y 좌표를 리턴하는 useScroll 커스텀 훅 추가

* 🚚 rename: hooks/css에 있던 useWindowSize 훅 폴더 위치 변경

* ✨ feat: 가상스크롤 구현 중

* 🙀 chore: 함수명 오타 수정

* ✨ feat: 가상스크롤 커스텀훅 추가

* ✨ feat: WorkspaceGrid 컴포넌트에 가상스크롤 적용

* 🙀 chore: grid의 총 높이 계산할 때 반올림   적용

* [#11] Google Analytics 적용 (#15)

* ✨ feat: ga4 코드 추가

* 🔨 refactor: 로컬 환경 디버깅용 코드로 전환

* 🙀 chore: GA 디버깅 코드 삭제

* ✨ feat: 이벤트 트레이싱

* Update boolock-dev-cicd.yml

* Update boolock-dev-cicd.yml

* [#18] useQuery, useInfiniteQuery -> useSuspenseQuery, useSuspenseInfiniteQuery 교체 (#19)

* 🔨 refactor: useQuery를 useSuspenseQuery로 교체 및 error 처러 코드 삭제

* 🔨 refactor: 로딩 UI 및 에러 처리 코드 삭제

* 🔨 refactor: suspense 적용 및 error boundy fallback에 workspace 에러 페이지 설정

* 🔨 refactor: useInfinityQuery -> useSuspenseQuery로 교체

* 🙀 chore: 400 에러와 404에러의 에러 코드 및 상태 메세지가 잘못 설정 되어 있어 올바르게 수정

* 🐛 fix: 기존 에러 발생 시 500 에러로만 응답하는 문제 해결

* 🔨 refactor: workspaceContainer에서 워크스페이스 데이터를 렌더링하고 페칭하는 부분을 분리함

* 🙀 chore: 컴포넌트 분리에 따른 코드 수정

* 🙀 chore: github action 에서 파일명 변경을 감지하지 못한 문제로 인해 파일명 변경

* 🙀 chore: 불필요한 console.log 삭제

* [#21] 홈페이지 로딩 시 스켈레톤UI에 그리드가 적용되지 않는 문제 해결, 워크스페이스 로딩 화면 조건부 렌더링 (#22)

* 🐛 fix: 스켈레톤UI에 그리드가 안되는 문제 해결

* ✨ feat: 워크스페이스 로딩 시간이 0.05초 이상일 때만 로딩 화면이 렌더링되도록 변경

* 🚨 !HOTFIX!: error가 발생할 때 return 해야하는데 그렇지 않을 때 return 하여 workspace가 초기화되지않는 문제 해결

* 🚨 !HOTFIX!: workflow on 설정 main push 일 경우로 수정

---------

Co-authored-by: YEONGJAE LEE <[email protected]>
Co-authored-by: Gyeungil Choi <[email protected]>
Co-authored-by: Yujin <[email protected]>
Co-authored-by: lee0jae330 <[email protected]>
  • Loading branch information
5 people authored Jan 17, 2025
1 parent dcd92f6 commit f2d576c
Show file tree
Hide file tree
Showing 39 changed files with 507 additions and 174 deletions.
15 changes: 8 additions & 7 deletions .github/workflows/boolock-dev-cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ name: ci/cd action

on:
push:
branches: ['dev']
pull_request:
branches: ['dev']
branches: ['main']

jobs:
build:
Expand Down Expand Up @@ -42,6 +40,7 @@ jobs:
- name: Set FE .env
run: |
echo "VITE_SERVER_URL=http://localhost:3000" > apps/client/.env
echo "VITE_MIXPANEL_TOKEN=${{ secrets.VITE_MIXPANEL_TOKEN }}" >> apps/client/.env
echo "VITE_STATIC_STORAGE_URL=${{ secrets.VITE_STATIC_STORAGE_URL }}" >> apps/client/.env
- name: Set Nginx SSL files
Expand Down Expand Up @@ -98,18 +97,20 @@ jobs:
password: ${{ secrets.SSH_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
script: |
cd boolock/web31-BooLock
git checkout dev
git pull origin dev
cd boolock/refactor-web31-BooLock
git checkout main
git pull origin main
echo "MONGO_URI=${{ secrets.DEPLOY_MONGO_URI }}" > apps/server/.env
echo "IS_LOCAL=false" >> apps/server/.env
echo "${{ secrets.DEPLOY_SERVER_CORS_ACCEPT }}" >> apps/server/.env
echo "SERVER_CORS_ACCEPT=${{ secrets.DEPLOY_SERVER_CORS_ACCEPT }}" >> apps/server/.env
echo "S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" >> apps/server/.env
echo "S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" >> apps/server/.env
echo "S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }}" >> apps/server/.env
echo "NODE_ENV=production" >> apps/server/.env
echo "VITE_SERVER_URL=${{ secrets.DEPLOY_VITE_SERVER_URL }}" > apps/client/.env
echo "VITE_MIXPANEL_TOKEN=${{ secrets.VITE_MIXPANEL_TOKEN }}" >> apps/client/.env
echo "VITE_STATIC_STORAGE_URL=${{ secrets.VITE_STATIC_STORAGE_URL }}" >> apps/client/.env
sudo docker compose pull
Expand Down
17 changes: 9 additions & 8 deletions apps/client/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
FROM base-image AS frontend-build

WORKDIR /app/apps/client
COPY ./apps/client .
COPY --from=base-image /app/packages /app/packages
RUN pnpm install --offline --frozen-lockfile
RUN pnpm run build
RUN pnpm install --offline --frozen-lockfile &&\
pnpm run build

FROM nginx:alpine AS frontend
COPY --from=frontend-build /app/apps/client/dist /usr/share/nginx/html

COPY /apps/client/nginx.conf /etc/nginx/conf.d/default.conf
COPY /apps/client/ssl /etc/nginx/ssl
RUN chmod 644 /etc/nginx/ssl/fullchain.pem &&\
chmod 600 /etc/nginx/ssl/privkey.pem &&\
chown -R nginx:nginx /usr/share/nginx/html /etc/nginx/ssl

RUN chmod -R 755 /usr/share/nginx/html
RUN chmod 644 /etc/nginx/ssl/fullchain.pem
RUN chmod 600 /etc/nginx/ssl/privkey.pem
RUN chown -R nginx:nginx /usr/share/nginx/html /etc/nginx/ssl
COPY --from=frontend-build /app/apps/client/dist /usr/share/nginx/html/
RUN chmod -R 755 /usr/share/nginx/html

EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
15 changes: 11 additions & 4 deletions apps/client/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
<!doctype html>
<html lang="ko">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SMXGZQRD0S"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-SMXGZQRD0S');
</script>
<!-- 기본 meta 태그 및 link -->
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/images/boolock_logo.png" />
Expand All @@ -14,10 +24,7 @@
<meta name="author" content="부스트캠프 9기 Web31팀" />
<!-- Open Graph -->
<meta property="og:title" content="BooLock" />
<meta
property="og:description"
content="쉽게 배우는 블록 코딩"
/>
<meta property="og:description" content="쉽게 배우는 블록 코딩" />
<meta property="og:url" content="https://boolock.site/" />
<meta property="og:image" content="/images/boolock_thumnail.png" />
<meta property="og:image:alt" content="블록코딩 플랫폼 BooLock의 썸네일" />
Expand Down
4 changes: 2 additions & 2 deletions apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
"codemirror": "^6.0.1",
"html2canvas": "^1.4.1",
"init": "^0.1.2",
"mixpanel-browser": "^2.58.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet-async": "^2.0.5",
"react-hot-toast": "^2.4.1",
"react-router-dom": "^6.27.0",
"react-spinners": "^0.14.1",
"react-youtube": "^10.1.0",
"storybook": "^8.4.1",
"uuid": "^11.0.3",
"zustand": "^5.0.1"
Expand All @@ -44,6 +44,7 @@
"@storybook/react": "8.4.2",
"@storybook/react-vite": "8.4.2",
"@storybook/test": "8.4.2",
"@types/mixpanel-browser": "^2.51.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/react-router-dom": "^5.3.3",
Expand All @@ -55,7 +56,6 @@
"eslint-plugin-react-refresh": "^0.4.14",
"eslint-plugin-storybook": "^0.11.0",
"postcss": "^8.4.47",
"prop-types": "15.8.1",
"tailwindcss": "^3.4.14",
"vite": "^5.4.10",
"vite-plugin-svgr": "^4.3.0"
Expand Down
20 changes: 14 additions & 6 deletions apps/client/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { DelayedFallback, Loading } from '@/shared/ui';
import { ErrorPage, WorkspaceErrorPage } from '@/pages';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import { ToasterWithMax } from '@/shared/ui';
import { ErrorPage } from '@/pages/ErrorPage/ErrorPage';
import { lazy, Suspense } from 'react';
import { Loading } from '@/shared/ui';
import { Suspense, lazy } from 'react';

import { Helmet } from 'react-helmet-async';
import { ToasterWithMax } from '@/shared/ui';

// lazy 로딩
const HomePage = lazy(() =>
Expand Down Expand Up @@ -47,12 +48,19 @@ const router = createBrowserRouter([
<title>BooLock - 작업 공간</title>
<meta name="description" content={`워크스페이스에서 HTML과 CSS를 연습해보세요.`} />
</Helmet>
<Suspense fallback={<Loading />}>

<Suspense
fallback={
<DelayedFallback>
<Loading />
</DelayedFallback>
}
>
<WorkspacePage />
</Suspense>
</>
),
errorElement: <ErrorPage />,
errorElement: <WorkspaceErrorPage />,
},
{
path: '*',
Expand Down
2 changes: 2 additions & 0 deletions apps/client/src/app/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { App } from './App';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { HelmetProvider } from 'react-helmet-async';
import { initMixpanel } from '@/shared/utils';

const container = document.getElementById('root');
const root = createRoot(container!);

const queryClient = new QueryClient();
initMixpanel();

root.render(
<StrictMode>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type CssCategoryButtonProps = {
*/
export const CssCategoryButton = ({ cssCategory }: CssCategoryButtonProps) => {
const { selectedCssCategory, setSelectedCssCategory } = useCssPropsStore();

return (
<button
key={cssCategory}
Expand Down
3 changes: 2 additions & 1 deletion apps/client/src/entities/workspace/SaveButton/SaveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as Blockly from 'blockly/core';
import { useCssPropsStore, useResetCssStore, useWorkspaceStore } from '@/shared/store';

import { Spinner } from '@/shared/ui';
import { capturePreview } from '@/shared/utils';
import { capturePreview, trackEvent } from '@/shared/utils';
import { cssStyleToolboxConfig } from '@/shared/blockly';
import toast from 'react-hot-toast';
import { useParams } from 'react-router-dom';
Expand All @@ -25,6 +25,7 @@ export const SaveButton = () => {
const [isCapture, setIsCapture] = useState<boolean>(false);

const handleClick = async () => {
trackEvent('workspace_saved');
try {
const canvas = Blockly.serialization.workspaces.save(workspace!) as any;
setIsCapture(true);
Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Banner, HomeHeader, WorkspaceContainer, WorkspaceModal } from '@/widgets';
import { Banner, HomeHeader, WorkspaceModal, WorkspaceSection } from '@/widgets';
import { useClassBlockStore, useLoadingStore, useWorkspaceStore } from '@/shared/store';

import { Loading } from '@/shared/ui';
Expand Down Expand Up @@ -26,7 +26,7 @@ export const HomePage = () => {
<div className="flex h-full w-full flex-col items-center">
<HomeHeader />
<Banner />
<WorkspaceContainer />
<WorkspaceSection />
<WorkspaceModal />
</div>
</>
Expand Down
14 changes: 14 additions & 0 deletions apps/client/src/pages/WorkspaceErrorPage/WorkspaceErrorPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ErrorPage, NotFound } from '@/pages';

import toast from 'react-hot-toast';
import { useRouteError } from 'react-router-dom';

export const WorkspaceErrorPage = () => {
const error: any = useRouteError();
const statusCode = error?.response?.statusCode || error?.status;
if (statusCode === 404) {
toast.error('워크스페이스 정보 불러오기 실패');
return <NotFound />;
}
return <ErrorPage />;
};
16 changes: 5 additions & 11 deletions apps/client/src/pages/Workspacepage/WorkspacePage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ImageTagModal, CoachMark, WorkspaceContent, WorkspacePageHeader } from '@/widgets';
import { CoachMark, ImageTagModal, WorkspaceContent, WorkspacePageHeader } from '@/widgets';
import { useEffect, useLayoutEffect } from 'react';
import { useGetWorkspace, usePreventLeaveWorkspacePage } from '@/shared/hooks';
import { Loading } from '@/shared/ui';
import { NotFound } from '@/pages/NotFound/NotFound';
import { useParams } from 'react-router-dom';
import { useLayoutEffect, useEffect } from 'react';

import { useCoachMarkStore } from '@/shared/store/useCoachMarkStore';
import { useParams } from 'react-router-dom';

/**
*
Expand All @@ -13,7 +12,7 @@ import { useCoachMarkStore } from '@/shared/store/useCoachMarkStore';
*/
export const WorkspacePage = () => {
const { workspaceId } = useParams();
const { isPending, isError } = useGetWorkspace(workspaceId as string);
useGetWorkspace(workspaceId as string);
usePreventLeaveWorkspacePage();
const { currentStep, isCoachMarkOpen, openCoachMark } = useCoachMarkStore();
const toolboxDiv = document.querySelector('.blocklyToolboxDiv');
Expand All @@ -36,14 +35,9 @@ export const WorkspacePage = () => {
}
}, [currentStep, toolboxDiv]);

if (isError) {
return <NotFound />;
}

return (
<>
<div className="flex h-screen flex-col">
{isPending && <Loading />}
{isCoachMarkOpen && <CoachMark />}
<WorkspacePageHeader />
<WorkspaceContent />
Expand Down
2 changes: 2 additions & 0 deletions apps/client/src/pages/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { HomePage } from './HomePage/HomePage';
export { NotFound } from './NotFound/NotFound';
export { WorkspacePage } from './Workspacepage/WorkspacePage';
export { ErrorPage } from './ErrorPage/ErrorPage';
export { WorkspaceErrorPage } from './WorkspaceErrorPage/WorkspaceErrorPage';
4 changes: 3 additions & 1 deletion apps/client/src/shared/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export { useSaveWorkspace } from './queries/useSaveWorkspace';
export { usePostImage } from './queries/usePostImage';
export { useDeleteImage } from './queries/useDeleteImage';

export { useWindowSize } from './css/useWindowSize';
export { useCssTooltip } from './css/useCssTooltip';
export { useCssOptions } from './css/useCssOptions';
export { useCssOptionItem } from './css/useCssOptionItem';
Expand All @@ -16,3 +15,6 @@ export { workspaceKeys } from './query-key/workspaceKeys';

export { usePreventLeaveWorkspacePage } from './usePreventLeaveWorkspacePage';
export { useInfiniteScroll } from './useInfiniteScroll';
export { useScrollPosition } from './useScrollPosition';
export { useWindowSize } from './useWindowSize';
export { useVirtualScroll } from './useVirtualScroll';
21 changes: 5 additions & 16 deletions apps/client/src/shared/hooks/queries/useGetWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ import { createUserId, getUserId, removeCssClassNamePrefix } from '@/shared/util
import {
useClassBlockStore,
useCssPropsStore,
useImageModalStore,
useResetCssStore,
useWorkspaceChangeStatusStore,
useWorkspaceStore,
useImageModalStore,
} from '@/shared/store';

import { WorkspaceApi } from '@/shared/api';
import toast from 'react-hot-toast';
import { useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useSuspenseQuery } from '@tanstack/react-query';
import { workspaceKeys } from '@/shared/hooks';

export const useGetWorkspace = (workspaceId: string) => {
Expand All @@ -24,29 +23,19 @@ export const useGetWorkspace = (workspaceId: string) => {
const { resetChangedStatusState } = useWorkspaceChangeStatusStore();
const { setIsResetCssChecked } = useResetCssStore();
const { setInitialImageMap, setInitialImageList } = useImageModalStore();
const { data, isPending, isError } = useQuery({
const { data, isPending, isError } = useSuspenseQuery({
queryKey: workspaceKeys.detail(workspaceId),
queryFn: () => {
resetChangedStatusState();
return workspaceApi.getWorkspace(userId, workspaceId);
},
});

useEffect(() => {
resetChangedStatusState();
}, []);

useEffect(() => {
if (isError) {
toast.error('워크스페이스 정보 불러오기 실패');
return;
}
if (!data) {
if (isError || !data || !data.workspaceDto) {
return;
}

if (!data.workspaceDto) {
return;
}
setName(data.workspaceDto.name);
Object.keys(data.workspaceDto.totalCssPropertyObj).forEach((className) => {
createCssClassBlock(className);
Expand Down
7 changes: 4 additions & 3 deletions apps/client/src/shared/hooks/queries/useGetWorkspaceList.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { WorkspaceApi } from '@/shared/api';
import { createUserId, getUserId } from '@/shared/utils';
import { useInfiniteQuery } from '@tanstack/react-query';

import { WorkspaceApi } from '@/shared/api';
import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
import { workspaceKeys } from '@/shared/hooks';
export const useGetWorkspaceList = () => {
const workspaceApi = WorkspaceApi();
Expand All @@ -12,7 +13,7 @@ export const useGetWorkspaceList = () => {
isFetchingNextPage,
isError,
data: workspaceList,
} = useInfiniteQuery({
} = useSuspenseInfiniteQuery({
queryKey: workspaceKeys.list(),
queryFn: async ({ pageParam }) => {
const isNewUser = !getUserId();
Expand Down
25 changes: 25 additions & 0 deletions apps/client/src/shared/hooks/useScrollPosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useEffect, useRef, useState } from 'react';

export const useScrollPosition = () => {
const [scrollPosition, setScrollPosition] = useState<number>(0);
const ticking = useRef(false);

useEffect(() => {
const onScroll = () => {
if (!ticking.current) {
requestAnimationFrame(() => {
setScrollPosition(window.scrollY);
ticking.current = false;
});
ticking.current = true;
}
};
window.addEventListener('scroll', onScroll);

return () => {
window.removeEventListener('scroll', onScroll);
};
}, []);

return { scrollPosition };
};
Loading

0 comments on commit f2d576c

Please sign in to comment.