Compare commits

...

10 Commits

Author SHA1 Message Date
928250a291 issue # 라우팅 이슈 수정 2025-11-11 15:05:26 +09:00
81ce79957d issue # 로그인 페이지 구현 중 2025-11-10 23:47:12 +00:00
9e37299966 issue # 로그인 화면 구현 중 2025-11-10 23:40:15 +00:00
c7ff6f8d13 issue # 로그인 화면 구현 중 2025-11-10 23:33:35 +00:00
민건희
a51566ffae issue # 로그인 페이지 라우팅 2025-11-10 23:08:33 +09:00
ca1ebd2985 issue # gitlab ci test 2025-11-10 07:28:31 +00:00
2f7b1d4653 issue # gitlab-ci test 2025-11-10 07:22:03 +00:00
ad9226a2f1 issue # gitlab ci test 2025-11-10 07:19:26 +00:00
4173cbef9b issue # gitlab ci test 2025-11-10 06:58:43 +00:00
58bdea1e60 issue # gitlab ci test 2025-11-10 06:37:07 +00:00
7 changed files with 132 additions and 19 deletions

View File

@@ -6,17 +6,20 @@ variables:
GIT_CHECKOUT: "true" GIT_CHECKOUT: "true"
GIT_SSL_NO_VERIFY: "true" GIT_SSL_NO_VERIFY: "true"
cache:
paths:
- node_modules/
build: build:
stage: build stage: build
image: node:25.1.0 image: node:25.1.0
tags: tags:
- local-runner - local-runner
script: script:
- export PATH="$PATH:$NVM_HOME/versions/node/v25.1.0/bin"
- [ -s "$NVM_HOME/nvm.sh" ] && \. "$NVM_HOME/nvm.sh"
- node -v - node -v
- npm install - npm install
- npm run build - npm run build
- pwd - pwd
- ls - ls
- docker cp -r $pwd/dist/* scheduler_front:/usr/share/nginx/html/ - whoami
- sudo cp -r $PWD/dist/. $DOCKER_VOLUME/scheduler/front/service/

19
package-lock.json generated
View File

@@ -108,6 +108,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5", "@babel/generator": "^7.28.5",
@@ -3448,6 +3449,7 @@
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~7.16.0" "undici-types": "~7.16.0"
} }
@@ -3458,6 +3460,7 @@
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
@@ -3468,6 +3471,7 @@
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
} }
@@ -3518,6 +3522,7 @@
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/scope-manager": "8.46.2",
"@typescript-eslint/types": "8.46.2", "@typescript-eslint/types": "8.46.2",
@@ -3770,6 +3775,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -3937,6 +3943,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.8.19", "baseline-browser-mapping": "^2.8.19",
"caniuse-lite": "^1.0.30001751", "caniuse-lite": "^1.0.30001751",
@@ -4307,7 +4314,8 @@
"version": "8.6.0", "version": "8.6.0",
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/embla-carousel-react": { "node_modules/embla-carousel-react": {
"version": "8.6.0", "version": "8.6.0",
@@ -4414,6 +4422,7 @@
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@@ -5578,6 +5587,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@@ -5657,6 +5667,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -5687,6 +5698,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.27.0" "scheduler": "^0.27.0"
}, },
@@ -5699,6 +5711,7 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.0.tgz", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.0.tgz",
"integrity": "sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==", "integrity": "sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
}, },
@@ -6155,6 +6168,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -6222,6 +6236,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -6394,6 +6409,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@@ -6485,6 +6501,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },

View File

@@ -7,6 +7,7 @@ import Layout from './layouts/Layout';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { useAuthStore } from './store/authStore'; import { useAuthStore } from './store/authStore';
import { PageRouting } from './data/RoutingData'; import { PageRouting } from './data/RoutingData';
import LoginPage from './ui/page/login/LoginPage';
function App() { function App() {
const { authData } = useAuthStore(); const { authData } = useAuthStore();
@@ -15,8 +16,9 @@ function App() {
<Router> <Router>
<Routes> <Routes>
<Route element={<Layout />}> <Route element={<Layout />}>
<Route element={<LoginPage />} path={PageRouting["LOGIN"].path} />
<Route element={<SignUpPage />} path={PageRouting["SIGN_UP"].path} /> <Route element={<SignUpPage />} path={PageRouting["SIGN_UP"].path} />
{!(authData?.isLogedIn) ? <Route element={<Navigate to={`/${PageRouting["SIGN_UP"].path}`} />} path="*" /> : null} {!(authData?.isLogedIn) ? <Route element={<Navigate to={PageRouting["LOGIN"].path} />} path="*" /> : null}
</Route> </Route>
</Routes> </Routes>
</Router> </Router>

View File

@@ -4,17 +4,17 @@ type PageRoutingInfo = {
} }
export const PageRouting: Record<string, PageRoutingInfo> = { export const PageRouting: Record<string, PageRoutingInfo> = {
LOGIN: { path: "login", title: "" }, LOGIN: { path: "/login", title: "" },
SIGN_UP: { path: "signup", title: "" }, SIGN_UP: { path: "/signup", title: "" },
RESET_PASSWORD: { path: "reset-password", title: "" }, RESET_PASSWORD: { path: "/reset-password", title: "" },
HOME: { path: "home", title: "홈" }, HOME: { path: "/home", title: "홈" },
SCHEDULES: { path: "schedules", title: "일정" }, SCHEDULES: { path: "/schedules", title: "일정" },
SCHEDULES_NEW: { path: "schedules/new", title: "일정 생성" }, SCHEDULES_NEW: { path: "/schedules/new", title: "일정 생성" },
SCHEDULES_EDIT: { path: "schedules/edit", title: "일정 수정" }, SCHEDULES_EDIT: { path: "/schedules/edit", title: "일정 수정" },
SCHEDULES_DETAIL: { path: "schedules/detail", title: "일정 상세" }, SCHEDULES_DETAIL: { path: "/schedules/detail", title: "일정 상세" },
USER_INFO: { path: "info", title: "사용자 정보" }, USER_INFO: { path: "/info", title: "사용자 정보" },
USER_FOLLOWING: { path: "info/following", title: "팔로잉 목록" }, USER_FOLLOWING: { path: "/info/following", title: "팔로잉 목록" },
USER_FOLLOWER: { path: "info/follower", title: "팔로워 목록" }, USER_FOLLOWER: { path: "/info/follower", title: "팔로워 목록" },
SETTINGS: { path: "settings", title: "설정" }, SETTINGS: { path: "/settings", title: "설정" },
NOT_FOUD: { path: "not-found", title: "존재하지 않는 페이지" }, NOT_FOUD: { path: "/not-found", title: "존재하지 않는 페이지" },
} as const; } as const;

View File

@@ -3,6 +3,7 @@ import * as z from 'zod';
export const LoginSchema = z.object({ export const LoginSchema = z.object({
email: z email: z
.email() .email()
.min(1, "이메일을 입력해주십시오.")
, password: z , password: z
.string() .string()
.min(8, "비밀번호는 8-12 자리여야 합니다.") .min(8, "비밀번호는 8-12 자리여야 합니다.")

View File

@@ -0,0 +1,91 @@
import { Card, CardContent, CardHeader, CardFooter } from '@/components/ui/card';
import { LoginSchema } from '@/data/form';
import { Field, FieldError, FieldGroup, FieldLabel, FieldLegend } from '@/components/ui/field';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { zodResolver } from '@hookform/resolvers/zod';
import { useState, useCallback } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { PageRouting } from '@/data/RoutingData';
import * as z from 'zod';
export default function LoginPage() {
const navigate = useNavigate();
const loginForm = useForm<z.infer<typeof LoginSchema>>({
resolver: zodResolver(LoginSchema),
defaultValues: {
email: "",
password: ""
}
});
const moveToSignUpPage = useCallback(() => {
navigate(PageRouting["SIGN_UP"].path);
}, []);
return (
<div className="w-full h-full flex flex-col justify-center items-center">
<Card className="w-md pl-2 pr-2">
<CardHeader>
</CardHeader>
<CardContent>
<form id="form-login">
<Controller
name="email"
control={loginForm.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="form-login-email"></FieldLabel>
<Input
{...field}
type="email"
id="form-login-email"
aria-invalid={fieldState.invalid}
/>
<FieldError errors={[fieldState.error]} />
</Field>
)}
>
</Controller>
<Controller
name="password"
control={loginForm.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="form-login-password"></FieldLabel>
<Input
{...field}
type="password"
id="form-login-password"
aria-invalid={fieldState.invalid}
/>
<FieldError errors={[fieldState.error]} />
</Field>
)}
>
</Controller>
</form>
</CardContent>
<CardFooter
className="w-full flex flex-col items-center justify-between gap-[5px]"
>
<Button
className="w-full"
type="submit"
form="form-login">
</Button>
<Button
className="w-full bg-white border-2 text-black hover:bg-gray-100"
onClick={moveToSignUpPage}
>
</Button>
</CardFooter>
</Card>
</div>
)
}

View File

@@ -2,7 +2,6 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite' import tailwindcss from '@tailwindcss/vite'
import path from 'path' import path from 'path'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [