Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

도커 컴포즈 프로덕션 세팅 #142

Merged
merged 20 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
af8f5f7
🐞 Fix(server): private 관련 리포지토리 버그 해결
paulcjy Nov 24, 2024
dea3348
✨ Feat(server): prisma exception filter 추가
paulcjy Nov 24, 2024
5b70d1c
🐛 Design(hub): ui 변경(LoadingSpinner, Tag)
paulcjy Nov 24, 2024
1568037
🐛 Design(hub): 아이콘 사이즈 축소
paulcjy Nov 24, 2024
d45556a
✨ Feat(hub): swr 적용
paulcjy Nov 24, 2024
00eade8
🚚 Setting(hub): tailwind config에 ui 디렉토리 추가
paulcjy Nov 24, 2024
1d4d217
🤖 Refactor(server): 폴더 구조 및 이름 변경
paulcjy Nov 24, 2024
e38bcfb
✨ Feat(server): private 관련 기능 완성
paulcjy Nov 25, 2024
9a0e590
🤖 Refactor(prisma): 데이터베이스 스키마 수정
paulcjy Nov 25, 2024
01036f9
🤖 Refactor(server): 통합한 모듈 삭제
paulcjy Nov 25, 2024
19880fe
✨ Feat(server): 로그인을 쿠키 방식으로 변경
paulcjy Nov 25, 2024
be4700e
✨ Feat(server): 마이 페이지 모듈 추가
paulcjy Nov 25, 2024
ef9c397
🤖 Refactor(server): public 관련 기본 틀 작성
paulcjy Nov 25, 2024
d471163
Merge branch 'feature/refactor-server-cjy' of https://github.com/boos…
SeoGeonhyuk Nov 26, 2024
0f5083c
🚚 Setting(infra): 인프라에 관련되서 기존에 작업했던 파일 추가
SeoGeonhyuk Nov 26, 2024
49dddf1
💄 Style: 포맷 적용
SeoGeonhyuk Nov 26, 2024
2a1bf0b
🚚 Setting(infra): 테라폼을 통한 nat gateway 및 private server 구축 및 테스트 완료
SeoGeonhyuk Nov 26, 2024
c61ece8
🚚 Setting(infra): private server가 기존에 만들어둔 멤버 서버 이미지를 활용해서 SSD 스왑을 자동…
SeoGeonhyuk Nov 26, 2024
3ccfaac
🤖 Refactor(docker-composes): 도커 컴포즈 프로덕션 환경 구성 중
SeoGeonhyuk Nov 26, 2024
49fae07
🚚 Setting(docker-composes): 도커 컴포즈 환경 변수 설정
SeoGeonhyuk Nov 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pnpm-lock.yaml
pnpm-lock.yaml
infra/
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ Cloud-Canvas는 이러한 문제점을 해결하기 위한 GUI 기반 인프라
## 📌 아키텍쳐

### 전반적인 인프라

![image](https://github.com/user-attachments/assets/5901b688-0d3d-4698-ad22-a4d4bb7aa8fd)

### CI/CD

<img width="1024" alt="cicd" src="https://github.com/user-attachments/assets/286d7d2d-bb6a-4315-bcff-4a6ea7569077">

# 팀
Expand Down
3 changes: 2 additions & 1 deletion apps/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"dependencies": {
"next": "15.0.3",
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106"
"react-dom": "19.0.0-rc-66855b96-20241106",
"swr": "^2.2.5"
},
"devDependencies": {
"@faker-js/faker": "^9.2.0",
Expand Down
13 changes: 13 additions & 0 deletions apps/hub/src/api/architectures.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { mockFetch } from '@/mocks/architectures';

export interface ArchitectureQueryParams {
page?: number;
search?: string;
sort?: string;
order?: string;
}

export const fetchArchitectures = async (params: ArchitectureQueryParams) => {
const response = await mockFetch(params);
return response;
};
2 changes: 1 addition & 1 deletion apps/hub/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
import { ArchitectureBoard } from '@/components/ArchitectureBoard';

export default function Home() {
return <ArchitectureBoard />;
return <ArchitectureBoard apiUrl="/public-architectures" />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const ArchitectureItem = ({
<div>{createdAt}</div>
<div className="ml-2">{author}</div>
</div>
<div className="flex text-[10px] gap-2 mt-1">
<div className="flex gap-2 mt-1">
{tags.map((tag) => (
<Tag key={tag} tag={tag} />
))}
Expand Down
6 changes: 3 additions & 3 deletions apps/hub/src/components/ArchitectureBoard/BoardHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export const BoardHeader = ({

const getSortIcon = (columnKey: string) => {
if (sort === columnKey) {
if (order === 'asc') return <ArrowUpIcon size={10} />;
if (order === 'desc') return <ArrowDownIcon size={10} />;
if (order === 'asc') return <ArrowUpIcon size={8} />;
if (order === 'desc') return <ArrowDownIcon size={8} />;
}
return <SortIcon />;
return <SortIcon size={9} />;
};

return (
Expand Down
60 changes: 20 additions & 40 deletions apps/hub/src/components/ArchitectureBoard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,19 @@
import { useUrlState } from '@/hooks/useUrlState';
import { mockFetch } from '@/mocks/architectures';
import { ErrorMessage } from '@/ui/ErrorMessage';
import { LoadingSpinner } from '@/ui/LoadingSpinner';
import { useCallback, useEffect, useState } from 'react';
import { useQueryData } from '@/hooks/useQueryData';
import { useQueryParams } from '@/hooks/useQueryParams';
import { SearchBar } from '@/components/SearchBar';
import { BoardHeader } from './BoardHeader';
import { ArchitectureList } from './ArchitectureList';
import { Pagination } from '../Pagination';
import { calculateTotalPages } from '@/utils/pagination';

export const ArchitectureBoard = () => {
const [params, setParams] = useUrlState();
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [architectures, setArchitectures] = useState<ArchitectureResponse>({
total: 0,
data: [],
});

const fetchArchitectures = useCallback(async () => {
setIsLoading(true);
setError(null);

try {
const response = await mockFetch(params);
setArchitectures(response);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
setArchitectures({ total: 0, data: [] });
} finally {
setIsLoading(false);
}
}, [params]);

useEffect(() => {
fetchArchitectures();
}, [params]);
export const ArchitectureBoard = ({ apiUrl }: { apiUrl: string }) => {
const { params, setParams } = useQueryParams();
const { data, total, isLoading, error } = useQueryData(apiUrl, params);

const handleSearch = (keyword: string) =>
setParams({ search: keyword, page: 1 });
setParams({ search: keyword, page: 1, sort: '', order: '' });

const handlePageChange = (page: number) => setParams({ page });

Expand All @@ -48,23 +23,28 @@ export const ArchitectureBoard = () => {
setParams({ sort: column, order: newOrder, page: 1 });
};

if (isLoading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
if (error) return <ErrorMessage message={(error as Error).message} />;

return (
<div className="container mx-auto px-4">
<div className="max-w-5xl mx-auto px-4">
<SearchBar onSearch={handleSearch} />
<BoardHeader
sort={params.sort}
order={params.order}
onSort={handleSort}
/>
<ArchitectureList data={architectures.data} />
<Pagination
currentPage={params.page}
totalPages={calculateTotalPages(architectures.total)}
onPageChange={handlePageChange}
/>
{isLoading ? (
<LoadingSpinner />
) : (
<>
<ArchitectureList data={data ?? []} />
<Pagination
currentPage={params.page}
totalPages={calculateTotalPages(total ?? 0)}
onPageChange={handlePageChange}
/>
</>
)}
</div>
);
};
35 changes: 35 additions & 0 deletions apps/hub/src/hooks/useQueryData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import useSWR from 'swr';
import { fetcher } from '@/utils/fetcher';

export const useQueryData = (
apiUrl: string,
params: {
page?: number;
limit?: number;
search?: string;
sort?: string;
order?: string;
},
) => {
const buildUrl = () => {
const searchParams = new URLSearchParams();
searchParams.append('page', params.page?.toString() ?? '1');
searchParams.append('limit', params.limit?.toString() ?? '10');
if (params.search) searchParams.append('search', params.search);
if (params.sort) searchParams.append('sort', params.sort);
if (params.order) searchParams.append('order', params.order);
return `${apiUrl}?${searchParams.toString()}`;
};

console.log('buildUrl', buildUrl());

const { data, error, isLoading, mutate } = useSWR(buildUrl, fetcher);

return {
data: data?.data ?? [],
total: data?.total ?? 0,
isLoading,
error,
mutate,
};
};
37 changes: 37 additions & 0 deletions apps/hub/src/hooks/useQueryParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useRouter, useSearchParams } from 'next/navigation';
import { useCallback } from 'react';

export const useQueryParams = () => {
const router = useRouter();
const searchParams = useSearchParams();

const getQueryParams = useCallback(() => {
return {
page: Number(searchParams.get('page')) || 1,
limit: Number(searchParams.get('limit')) || 10,
search: searchParams.get('search') || '',
sort: searchParams.get('sort') || '',
order: searchParams.get('order') || '',
};
}, [searchParams]);

const setQueryParams = useCallback(
(newParams: Record<string, string | number>) => {
const current = new URLSearchParams(searchParams.toString());
Object.entries(newParams).forEach(([key, value]) => {
if (value) {
current.set(key, String(value));
} else {
current.delete(key);
}
});
router.push(`?${current.toString()}`);
},
[router, searchParams],
);

return {
params: getQueryParams(),
setParams: setQueryParams,
};
};
48 changes: 0 additions & 48 deletions apps/hub/src/hooks/useUrlState.ts

This file was deleted.

18 changes: 11 additions & 7 deletions apps/hub/src/mocks/architectures.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ITEMS_PER_PAGE } from '@/data/constants';
import { Architecture, ArchitectureResponse, SearchParams } from '@/types';
import { Architecture, ArchitectureResponse } from '@/types';
import { faker } from '@faker-js/faker';

export const mockArchitectures = Array(0);
Expand Down Expand Up @@ -46,12 +46,16 @@ for (let i = 2; i <= 138; i++) {
});
}

export const mockFetch = async ({
page = 1,
search = '',
sortBy = '',
order = '',
}: Partial<SearchParams>): Promise<ArchitectureResponse> => {
export const mockFetch = async (
apiUrl: string,
): Promise<ArchitectureResponse> => {
const [, params] = apiUrl.split('?');
const searchParams = new URLSearchParams(params);
const page = Number(searchParams.get('page')) || 1;
const search = searchParams.get('search') || '';
const sortBy = searchParams.get('sort') || '';
const order = searchParams.get('order') || '';

let filteredData = [...mockArchitectures];

if (search) {
Expand Down
19 changes: 17 additions & 2 deletions apps/hub/src/ui/LoadingSpinner.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
export const LoadingSpinner = () => (
<div className="flex justify-center py-10">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" />
<div className="flex justify-center py-20">
<svg
aria-hidden="true"
className="w-10 h-10 text-gray-200 animate-spin fill-blue-600"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
</div>
);
4 changes: 2 additions & 2 deletions apps/hub/src/ui/Tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Link from 'next/link';

export const Tag = ({ tag }: { tag: string }) => (
<Link
href={`/tags?keyword=${tag}`}
className="bg-purple-100/60 text-purple-600 border border-purple-300 rounded-md px-1.5"
href={`/tags/${tag}`}
className="text-[10px] bg-purple-100/60 text-purple-600 border border-purple-300 rounded-md px-1.5"
>
{tag}
</Link>
Expand Down
8 changes: 8 additions & 0 deletions apps/hub/src/utils/fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { mockFetch } from '@/mocks/architectures';

export const fetcher = async (url: string) => {
const res = await mockFetch(url);
// if (!res.ok) throw new Error('Failed to fetch data');
// return res.json();
return res;
};
1 change: 1 addition & 0 deletions apps/hub/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default {
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
'./src/ui/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
Expand Down
Loading
Loading