From c7826afe3dfb598c0918ab3073522b57e5d90340 Mon Sep 17 00:00:00 2001 From: seungdeok Date: Sun, 12 Oct 2025 13:04:42 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat(web):=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20SNSLoginButton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/public/icons/google.svg | 7 +++ apps/web/public/icons/kakao.svg | 4 ++ .../views/login/components/SNSLoginButton.tsx | 48 +++++++++++++++++++ apps/web/src/views/login/index.tsx | 19 +------- apps/web/src/views/login/login.stories.tsx | 36 ++++++++++++++ 5 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 apps/web/public/icons/google.svg create mode 100644 apps/web/public/icons/kakao.svg create mode 100644 apps/web/src/views/login/components/SNSLoginButton.tsx create mode 100644 apps/web/src/views/login/login.stories.tsx diff --git a/apps/web/public/icons/google.svg b/apps/web/public/icons/google.svg new file mode 100644 index 0000000..1a7aebe --- /dev/null +++ b/apps/web/public/icons/google.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/icons/kakao.svg b/apps/web/public/icons/kakao.svg new file mode 100644 index 0000000..1319445 --- /dev/null +++ b/apps/web/public/icons/kakao.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/src/views/login/components/SNSLoginButton.tsx b/apps/web/src/views/login/components/SNSLoginButton.tsx new file mode 100644 index 0000000..1931550 --- /dev/null +++ b/apps/web/src/views/login/components/SNSLoginButton.tsx @@ -0,0 +1,48 @@ +"use client"; + +import Image from "next/image"; + +export function SNSLoginButton({ provider }: { provider: "google" | "kakao" }) { + const variants = { + google: { + label: "구글 로그인", + bg: "#FFFFFF", + color: "#000000", + border: "1px solid #E3E8EF", + }, + kakao: { + label: "카카오 로그인", + bg: "#FEE500", + color: "#000000", + border: "1px solid #FEE500", + }, + }; + + return ( + + ); +} diff --git a/apps/web/src/views/login/index.tsx b/apps/web/src/views/login/index.tsx index 5f5af4f..bb1e1ed 100644 --- a/apps/web/src/views/login/index.tsx +++ b/apps/web/src/views/login/index.tsx @@ -1,11 +1,6 @@ "use client"; -import GoogleButton from "@/widgets/login/GoogleButton"; -import { useSession } from "next-auth/react"; - export function LoginPage() { - const { data: session } = useSession(); - return (
- -
-
-          {JSON.stringify(session, null, 2)}
-        
-
+
Login
); } diff --git a/apps/web/src/views/login/login.stories.tsx b/apps/web/src/views/login/login.stories.tsx new file mode 100644 index 0000000..8d1015d --- /dev/null +++ b/apps/web/src/views/login/login.stories.tsx @@ -0,0 +1,36 @@ +import type { Meta, StoryObj } from "@storybook/nextjs-vite"; +import { LoginPage } from "."; +import { SNSLoginButton } from "./components/SNSLoginButton"; + +const meta = { + title: "v2/Views/Login", + component: LoginPage, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: {}, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// 구글 로그인 +export const 구글_로그인: Story = { + args: {}, + render: () => ( +
+ +
+ ), +}; + +// 카카오 로그인 +export const 카카오_로그인: Story = { + args: {}, + render: () => ( +
+ +
+ ), +}; From 80ea91c59d3a6cd0d00784ea13ebc9aa977b9a76 Mon Sep 17 00:00:00 2001 From: seungdeok Date: Sun, 12 Oct 2025 13:26:58 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat(web):=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20TextField?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/login/components/TextField.tsx | 107 ++++++++++++++++++ apps/web/src/views/login/login.stories.tsx | 54 +++++++++ 2 files changed, 161 insertions(+) create mode 100644 apps/web/src/views/login/components/TextField.tsx diff --git a/apps/web/src/views/login/components/TextField.tsx b/apps/web/src/views/login/components/TextField.tsx new file mode 100644 index 0000000..8cae931 --- /dev/null +++ b/apps/web/src/views/login/components/TextField.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { useId, useState } from "react"; + +interface TextFieldProps { + type: "text" | "password"; + label: string; + validate?: (value: string) => boolean; + onChange?: (value: string) => void; + value?: string; + className?: string; + style?: React.CSSProperties; + required?: boolean; + disabled?: boolean; + readonly?: boolean; + placeholder?: string; + error?: string; +} + +export function TextField({ + type, + label, + validate, + onChange, + value, + className, + style, + required, + disabled, + readonly, + placeholder, + error, +}: TextFieldProps) { + const id = useId(); + const [isFocused, setIsFocused] = useState(false); + const hasValidationError = validate ? !validate(value || "") : false; + const displayError = hasValidationError ? error : ""; + + const getBorderColor = () => { + if (hasValidationError) return "#E52929"; // 에러 상태 + if (isFocused) return "#3A8DFF"; // focus 상태 + return "#E3E8EF"; + }; + + return ( +
+ + ) => onChange(e.target.value) : undefined} + onFocus={() => setIsFocused(true)} + onBlur={() => setIsFocused(false)} + value={value} + required={required} + disabled={disabled} + readOnly={readonly} + placeholder={placeholder} + style={{ + width: "100%", + height: "43px", + borderBottom: `1px solid ${getBorderColor()}`, + display: "flex", + flexDirection: "row", + alignItems: "center", + color: "#111", + fontSize: "14px", + fontStyle: "normal", + fontWeight: "500", + lineHeight: "140%", + letterSpacing: "-0.28px", + outline: "none", + transition: "border-color 0.2s ease-in-out", + ...style, + }} + /> + + {displayError} + +
+ ); +} diff --git a/apps/web/src/views/login/login.stories.tsx b/apps/web/src/views/login/login.stories.tsx index 8d1015d..706f215 100644 --- a/apps/web/src/views/login/login.stories.tsx +++ b/apps/web/src/views/login/login.stories.tsx @@ -1,6 +1,8 @@ import type { Meta, StoryObj } from "@storybook/nextjs-vite"; +import React from "react"; import { LoginPage } from "."; import { SNSLoginButton } from "./components/SNSLoginButton"; +import { TextField } from "./components/TextField"; const meta = { title: "v2/Views/Login", @@ -34,3 +36,55 @@ export const 카카오_로그인: Story = { ), }; + +// 이메일 주소 입력 +export const 이메일_주소_입력: Story = { + args: {}, + render: () => { + const [email, setEmail] = React.useState(""); + + return ( +
+ { + // 빈 값이거나 유효한 이메일 형식인지 확인 + if (value.length === 0) return true; + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(value); + }} + /> +
+ ); + }, +}; + +// 비밀번호 입력 +export const 비밀번호_입력: Story = { + args: {}, + render: () => { + const [password, setPassword] = React.useState(""); + + return ( +
+ { + // 빈 값이거나 8자 이상인지 확인 + if (value.length === 0) return true; + return value.length >= 8; + }} + /> +
+ ); + }, +}; From f6905fdc5b1c81164655af165bfc72f69454a94e Mon Sep 17 00:00:00 2001 From: seungdeok Date: Sun, 12 Oct 2025 13:33:33 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat(web):=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20Button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/src/views/login/components/Button.tsx | 33 +++++++++++++++++++ apps/web/src/views/login/login.stories.tsx | 12 +++++++ 2 files changed, 45 insertions(+) create mode 100644 apps/web/src/views/login/components/Button.tsx diff --git a/apps/web/src/views/login/components/Button.tsx b/apps/web/src/views/login/components/Button.tsx new file mode 100644 index 0000000..59bf123 --- /dev/null +++ b/apps/web/src/views/login/components/Button.tsx @@ -0,0 +1,33 @@ +interface ButtonProps { + label: string; + style?: React.CSSProperties; + onClick?: () => void; + disabled?: boolean; + className?: string; +} + +export function Button({ label, style, onClick, disabled, className }: ButtonProps) { + return ( + + ); +} diff --git a/apps/web/src/views/login/login.stories.tsx b/apps/web/src/views/login/login.stories.tsx index 706f215..66f884a 100644 --- a/apps/web/src/views/login/login.stories.tsx +++ b/apps/web/src/views/login/login.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/nextjs-vite"; import React from "react"; import { LoginPage } from "."; +import { Button } from "./components/Button"; import { SNSLoginButton } from "./components/SNSLoginButton"; import { TextField } from "./components/TextField"; @@ -88,3 +89,14 @@ export const 비밀번호_입력: Story = { ); }, }; + +// 로그인 버튼 +export const 로그인_버튼: Story = { + args: {}, + render: () => ( +
+
+ ), +}; From d7022e474dede5f37a8748fbfd8181d3efb02f57 Mon Sep 17 00:00:00 2001 From: seungdeok Date: Sun, 12 Oct 2025 13:43:16 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat(web):=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=20=ED=8E=98=EC=9D=B4=EC=A7=80=20TextField?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/views/register/index.tsx | 17 +++ .../src/views/register/register.stories.tsx | 110 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 apps/web/src/views/register/index.tsx create mode 100644 apps/web/src/views/register/register.stories.tsx diff --git a/apps/web/src/views/register/index.tsx b/apps/web/src/views/register/index.tsx new file mode 100644 index 0000000..11a5dd7 --- /dev/null +++ b/apps/web/src/views/register/index.tsx @@ -0,0 +1,17 @@ +"use client"; + +export function RegisterPage() { + return ( +
+
Register
+
+ ); +} diff --git a/apps/web/src/views/register/register.stories.tsx b/apps/web/src/views/register/register.stories.tsx new file mode 100644 index 0000000..8a0a27d --- /dev/null +++ b/apps/web/src/views/register/register.stories.tsx @@ -0,0 +1,110 @@ +import type { Meta, StoryObj } from "@storybook/nextjs-vite"; +import React from "react"; +import { RegisterPage } from "."; +import { TextField } from "../login/components/TextField"; + +const meta = { + title: "v2/Views/Register", + component: RegisterPage, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: {}, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// 이메일 주소 입력 +export const 이메일_주소_입력: Story = { + args: {}, + render: () => { + const [email, setEmail] = React.useState(""); + + return ( +
+ { + // 빈 값이거나 유효한 이메일 형식인지 확인 + if (value.length === 0) return true; + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(value); + }} + /> +
+ ); + }, +}; + +// 비밀번호 입력 +export const 비밀번호_입력: Story = { + args: {}, + render: () => { + const [password, setPassword] = React.useState(""); + + return ( +
+ { + // 8~16자리 영대•소문자, 숫자, 특수문자 조합 + if (value.length === 0) return true; + return value.length >= 8 && value.length <= 16; + }} + /> + { + // 빈 값이거나 비밀번호와 일치하는지 확인 + if (value.length === 0) return true; + if (value !== password) return false; + return true; + }} + /> +
+ ); + }, +}; + +// 닉네임 입력 +export const 닉네임: Story = { + args: {}, + render: () => { + const [nickname, setNickname] = React.useState(""); + + return ( +
+ { + // 빈 값이거나 3자 이상인지 확인 + if (value.length === 0) return true; + return value.length >= 6; + }} + /> +
+ ); + }, +}; From 862b5400293726d5935f7337951b2bae5ec62758 Mon Sep 17 00:00:00 2001 From: seungdeok Date: Sun, 12 Oct 2025 13:44:04 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat(web):=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20TextField?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/views/login/login.stories.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/web/src/views/login/login.stories.tsx b/apps/web/src/views/login/login.stories.tsx index 66f884a..953c7d9 100644 --- a/apps/web/src/views/login/login.stories.tsx +++ b/apps/web/src/views/login/login.stories.tsx @@ -52,7 +52,7 @@ export const 이메일_주소_입력: Story = { placeholder="abcdef@naver.com" value={email} onChange={setEmail} - error="올바른 이메일 형식이 아닙니다." + error="잘못된 이메일 주소입니다." validate={value => { // 빈 값이거나 유효한 이메일 형식인지 확인 if (value.length === 0) return true; @@ -76,13 +76,14 @@ export const 비밀번호_입력: Story = { { - // 빈 값이거나 8자 이상인지 확인 + // 8~16자리 영대•소문자, 숫자, 특수문자 조합 if (value.length === 0) return true; - return value.length >= 8; + return value.length >= 8 && value.length <= 16; }} /> From ebdcba24dc81c2fac249def2d190540cb8ee1e21 Mon Sep 17 00:00:00 2001 From: seungdeok Date: Sun, 12 Oct 2025 13:48:50 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix(web):=20gap=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/views/register/register.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/views/register/register.stories.tsx b/apps/web/src/views/register/register.stories.tsx index 8a0a27d..3710ca6 100644 --- a/apps/web/src/views/register/register.stories.tsx +++ b/apps/web/src/views/register/register.stories.tsx @@ -50,7 +50,7 @@ export const 비밀번호_입력: Story = { const [password, setPassword] = React.useState(""); return ( -
+