프로젝트명
Vantech Event Finder
제작기간
2023.05.16 - 2023.08.30
개발스택
•
프론트엔드 : Nextjs 13 / T3 / Typescript / Mantine
•
백엔드 : Postgres, Prisma, TRPC
프로젝트 기획 및 소개
Vantech Event Finder 프로젝트는 밴쿠버에서 열리는 다양한 IT 이벤트를 쉽게 찾을 수 있도록 돕기 위해 기획되었습니다. 밴쿠버는 각종 취미 소모임과 다양한 네트워킹 이벤트가 매달 개최됩니다. 그에 맞게 Meet Up 어플이나 Eventbrite 등 모임을 찾을 수 있는 어플도 다양합니다. 그 중에도 IT 에 관련된 이벤트를 한 곳에 모아 쉽게 검색하고 관리할 수 있는 도구를 만들어 보자는 생각으로 프로젝트가 시작되었습니다
프로젝트 주요 기능
1. Google Map API
날짜, 위치, 카테고리 등 다양한 필터링 옵션을 제공하여 사용자가 원하는 이벤트를 빠르게 찾을 수 있도록 합니다. 주요 기능은 다음과 같습니다:
•
위치 기반 검색: 사용자가 현재 위치를 기준으로 주변 이벤트를 검색할 수 있습니다.
•
날짜별 검색: 특정 날짜나 기간 동안 열리는 이벤트를 검색할 수 있습니다.
•
이벤트 핀: 각 이벤트 위치에 핀을 표시하여 지도를 통해 이벤트 위치를 직관적으로 확인할 수 있습니다.
•
코드 예시
import { useState, useEffect } from "react";
import { useMantineColorScheme } from "@mantine/core";
import { mapTheme, loader } from "~/utils";
export const GoogleMaps = () => {
const [map, setMap] = useState<google.maps.Map>();
const { colorScheme } = useMantineColorScheme();
useEffect(() => {
const fetchMap = async () => {
await loader.load().then(() => {
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
const mapOptions = {
center: { lat: latitude, lng: longitude },
zoom: 16,
styles: colorScheme === "dark" ? mapTheme.dark : mapTheme.light,
};
const newMap = new window.google.maps.Map(
document.getElementById("map") as HTMLElement,
mapOptions
);
const marker = new window.google.maps.Marker({
position: { lat: latitude, lng: longitude },
map: newMap,
});
setMap(newMap);
}
});
});
};
void fetchMap();
}, [colorScheme]);
return <div id="map" style={{ height: "100%", width: "100%" }}></div>;
};
TypeScript
복사
2. next-auth 로 login & signup 기능 구현
로그인 기능은 next-auth를 사용하여 구현되었습니다. 사용자는 이메일과 비밀번호를 입력하여 로그인할 수 있으며 로그인 성공 시 홈 페이지로 리디렉션됩니다. 폼 유효성 검사 및 상태 관리를 위해 Mantine의 useForm 훅을 사용합니다. 로그인 요청은 signIn 함수를 통해 처리됩니다.
•
코드 예제
1.
next-auth 설정 파일 (app/api/auth/[...nextauth].ts)
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
export default NextAuth({
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" }
},
async authorize(crd) {
const user = await yourUserLoginFunction(crd.email, crd.password);
return user || null ;
}
})
],
session: { jwt: true },
// ...
});
TypeScript
복사
2.
로그인 페이지 (app/(auth)/login/page.tsx)
const LogIn: React.FC = () => {
const { data: session } = useSession();
const router = useRouter();
const [error, setError] = useState('');
const theme = useMantineTheme();
const form = useForm({
initialValues: {
email: '',
password: '',
rememberPassword: false,
},
validateInputOnChange: true,
validate: {
password: (value) => (value.length < 1 ? 'Please input Password' : null),
email: (value) => (value.length < 1 ? 'Please input email address' : null),
},
});
const isDisabled = Object.keys(form.errors).length !== 0;
const handleSubmit = async (values: { email: string; password: string; rememberPassword: boolean }) => {
try {
const result = await signIn('credentials', {
redirect: false,
email: values.email,
password: values.password,
});
if (!result?.error) {
if (!session) {
await signIn('credentials', {
redirect: false,
email: values.email,
password: values.password,
});
}
form.reset();
router.push('/');
} else {
form.reset();
throw new Error();
}
} catch (error) {
setError('Failed to login!');
}
};
if (session) router.push('/');
};
export default LogIn;
TypeScript
복사
3. Prisma를 사용한 Postgres 데이터베이스 설정
Prisma를 사용하여 Postgres 데이터베이스를 설정하고, 데이터베이스와의 상호작용을 단순화하였습니다.
이 프로젝트의 데이터베이스 관리에는 Prisma가 사용되었습니다. Prisma를 사용하여 Postgres 데이터베이스를 설정하고 사용자, 세션, 계정 및 관련된 인증 토큰에 타입을 부여해서 개발 생산성을 높였습니다.
•
코드 예시
// This is your Prisma schema file,
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ----- App Schema ------ //
model FavEvent {
id String @id @default(cuid())
userId String
date DateTime @default(now())
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
password String
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
}
// ...
TypeScript
복사