diff --git a/api/api.ts b/api/api.ts
index 19b88e21b..3c87182ea 100644
--- a/api/api.ts
+++ b/api/api.ts
@@ -1,5 +1,5 @@
import { USER_ID, totalFolderId } from '@/util/constants';
-import { Folder, FolderAPI, LinkTypes, LinkAPITypes, User } from '@/types/types';
+import type { Folder, FolderAPI, LinkTypes, LinkAPITypes, User } from '@/types/types';
const BASE_URL = 'https://bootcamp-api.codeit.kr/api';
diff --git a/components/common/FolderList.tsx b/components/common/FolderList.tsx
index 5e3632bff..2ac2748aa 100644
--- a/components/common/FolderList.tsx
+++ b/components/common/FolderList.tsx
@@ -1,7 +1,7 @@
import styled from 'styled-components';
import LinkCard from './LinkCard';
import { useContext, useEffect, useState } from 'react';
-import { LinkTypes } from '@/types/types';
+import type { LinkTypes } from '@/types/types';
import { LinkListContext } from '@/context/createContext.';
const List = styled.ul`
diff --git a/components/common/LinkCard.tsx b/components/common/LinkCard.tsx
index 333f12f33..6107a7046 100644
--- a/components/common/LinkCard.tsx
+++ b/components/common/LinkCard.tsx
@@ -2,7 +2,7 @@ import styled from 'styled-components';
import LinkImage, { ImageCard } from './LinkImage';
import LinkInfo, { InfoGroup } from './LinkInfo';
import Link from 'next/link';
-import { LinkTypes } from '@/types/types';
+import type { LinkTypes } from '@/types/types';
const LinkItem = styled.li`
width: calc(100% / 3 - 4rem / 3);
diff --git a/components/common/LinkInfo.tsx b/components/common/LinkInfo.tsx
index 7fa862fdd..4704cc156 100644
--- a/components/common/LinkInfo.tsx
+++ b/components/common/LinkInfo.tsx
@@ -3,7 +3,7 @@ import { format, formatDistanceToNowStrict } from 'date-fns';
import { useState, MouseEvent } from 'react';
import { modalTypes } from '../../util/constants';
import Popover from './Popover';
-import { LinkTypes } from '@/types/types';
+import type { LinkTypes } from '@/types/types';
export const InfoGroup = styled.div`
display: flex;
diff --git a/components/common/Popover.tsx b/components/common/Popover.tsx
index cb2d01a81..6fa72fb99 100644
--- a/components/common/Popover.tsx
+++ b/components/common/Popover.tsx
@@ -1,9 +1,10 @@
-import { useState, MouseEvent } from 'react';
+import { MouseEvent } from 'react';
import { createPortal } from 'react-dom';
import { modalTypes } from '@/util/constants';
import styles from './Popover.module.css';
import DeleteLink from './modal/DeleteLink';
import Add from './modal/Add';
+import { useOpenModal } from '@/hooks/modal';
interface PopoverProps {
url: string;
@@ -12,42 +13,27 @@ interface PopoverProps {
}
const Popover = ({ url, show, onPopoverClick }: PopoverProps) => {
- const [showDeleteModal, setShowDeleteModal] = useState(false);
- const [showAddModal, setShowAddModal] = useState(false);
-
- const handleShowDeleteModal = () => {
- setShowDeleteModal(true);
- };
-
- const handleShowAddModal = () => {
- setShowAddModal(true);
- };
-
- const handleCloseDeleteModal = () => {
- setShowDeleteModal(false);
- };
-
- const handleCloseAddModal = () => {
- setShowAddModal(false);
- };
+ const {
+ isOpenModal: isOpenDeleteModal,
+ openModal: openDeleteModal,
+ closeModal: closeDeleteModal,
+ } = useOpenModal(false);
+ const { isOpenModal: isOpenAddModal, openModal: openAddModal, closeModal: closeAddModal } = useOpenModal(false);
return (
<>
{show && (
-
)}
- {showDeleteModal && createPortal(, document.body)}
- {showAddModal && createPortal(, document.body)}
+ {isOpenDeleteModal && createPortal(, document.body)}
+ {isOpenAddModal && createPortal(, document.body)}
>
);
};
diff --git a/components/common/modal/Add.tsx b/components/common/modal/Add.tsx
index 0bd742fa0..8f2260a2e 100644
--- a/components/common/modal/Add.tsx
+++ b/components/common/modal/Add.tsx
@@ -4,7 +4,7 @@ import Title from './title/Title';
import SubmitButton from './submitButton/SubmitButton';
import styles from './Add.module.css';
import { FoldersContext } from '@/context/createContext.';
-import { Modal } from '@/types/types';
+import type { Modal } from '@/types/types';
interface AddProps {
link: string;
diff --git a/components/common/modal/AddFolder.tsx b/components/common/modal/AddFolder.tsx
index 975871f26..f351a752a 100644
--- a/components/common/modal/AddFolder.tsx
+++ b/components/common/modal/AddFolder.tsx
@@ -1,8 +1,8 @@
-import { Modal } from '@/types/types';
import FolderNameInput from './folderNameInput/folderNameInput';
import Frame from './frame/Frame';
import SubmitButton from './submitButton/SubmitButton';
import Title from './title/Title';
+import type { Modal } from '@/types/types';
interface AddFolderProps {
onCloseModal: Modal['closeModal'];
diff --git a/components/common/modal/DeleteFolder.tsx b/components/common/modal/DeleteFolder.tsx
index 998370586..5eeb6794a 100644
--- a/components/common/modal/DeleteFolder.tsx
+++ b/components/common/modal/DeleteFolder.tsx
@@ -1,7 +1,7 @@
import Title from './title/Title';
import Frame from './frame/Frame';
import SubmitButton from './submitButton/SubmitButton';
-import { Modal } from '@/types/types';
+import type { Modal } from '@/types/types';
interface DeleteFolder {
selectedFolderName: string;
diff --git a/components/common/modal/DeleteLink.tsx b/components/common/modal/DeleteLink.tsx
index 31ba80d20..22c1f429b 100644
--- a/components/common/modal/DeleteLink.tsx
+++ b/components/common/modal/DeleteLink.tsx
@@ -1,4 +1,4 @@
-import { Modal } from '@/types/types';
+import type { Modal } from '@/types/types';
import Frame from './frame/Frame';
import SubmitButton from './submitButton/SubmitButton';
import Title from './title/Title';
diff --git a/components/common/modal/Edit.tsx b/components/common/modal/Edit.tsx
index 3e4a19162..34105507d 100644
--- a/components/common/modal/Edit.tsx
+++ b/components/common/modal/Edit.tsx
@@ -1,4 +1,4 @@
-import { Modal } from '@/types/types';
+import type { Modal } from '@/types/types';
import FolderNameInput from './folderNameInput/folderNameInput';
import Frame from './frame/Frame';
import SubmitButton from './submitButton/SubmitButton';
diff --git a/components/common/modal/Share.tsx b/components/common/modal/Share.tsx
index 76b7cc193..0a0ee48fd 100644
--- a/components/common/modal/Share.tsx
+++ b/components/common/modal/Share.tsx
@@ -6,7 +6,7 @@ import styles from './Share.module.css';
import ShareBtn from './shareBtn/ShareBtn';
import { sampleUrl } from '@/util/constants';
import { useKaKaoScript } from '@/hooks/kakao';
-import { Modal } from '@/types/types';
+import type { Modal } from '@/types/types';
interface ShareProps {
currentId: string;
diff --git a/components/common/modal/frame/Frame.tsx b/components/common/modal/frame/Frame.tsx
index 55ec6b83a..fd7ef508f 100644
--- a/components/common/modal/frame/Frame.tsx
+++ b/components/common/modal/frame/Frame.tsx
@@ -1,7 +1,7 @@
import { MouseEvent, PropsWithChildren, ReactNode } from 'react';
import styles from './Frame.module.css';
import { exitBtnId, modalBackground } from '@/util/constants';
-import { Modal } from '@/types/types';
+import type { Modal } from '@/types/types';
interface FrameProps {
onCloseModal: Modal['closeModal'];
diff --git a/components/layout/Layout.tsx b/components/layout/Layout.tsx
index 617016e4e..d11f46abc 100644
--- a/components/layout/Layout.tsx
+++ b/components/layout/Layout.tsx
@@ -1,8 +1,15 @@
import Header from './header/Header';
import Footer from './footer/Footer';
import { PropsWithChildren } from 'react';
+import { useRouter } from 'next/router';
function Layout({ children }: PropsWithChildren) {
+ const { pathname } = useRouter();
+
+ if (pathname === '/signin' || pathname === '/signup') {
+ return <>{children}>;
+ }
+
return (
<>
diff --git a/components/layout/footer/IconWithLink.tsx b/components/layout/footer/IconWithLink.tsx
index 64e95c918..360f891f5 100644
--- a/components/layout/footer/IconWithLink.tsx
+++ b/components/layout/footer/IconWithLink.tsx
@@ -1,4 +1,4 @@
-import { FooterItem } from '@/types/types';
+import type { FooterItem } from '@/types/types';
import styles from './IconWithLink.module.css';
import Link from 'next/link';
import Image from 'next/image';
diff --git a/components/layout/header/Header.tsx b/components/layout/header/Header.tsx
index 4c9250766..4980e984c 100644
--- a/components/layout/header/Header.tsx
+++ b/components/layout/header/Header.tsx
@@ -2,7 +2,7 @@ import Link from 'next/link';
import styles from './Header.module.css';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
-import { User } from '@/types/types';
+import type { User } from '@/types/types';
import { getUser } from '@/api/api';
import Image from 'next/image';
@@ -38,7 +38,7 @@ function Header() {
{user.email}
>
) : (
-
+
로그인
)}
diff --git a/components/pages/folder/LinkList.tsx b/components/pages/folder/LinkList.tsx
index 554b70c40..a2a53df34 100644
--- a/components/pages/folder/LinkList.tsx
+++ b/components/pages/folder/LinkList.tsx
@@ -1,9 +1,9 @@
import styled from 'styled-components';
-import { useContext, useEffect, useState } from 'react';
+import { useContext } from 'react';
import UpdateBtnList from './UpdateBtnList';
-import { Folder, LinkTypes } from '@/types/types';
+import type { Folder } from '@/types/types';
import { totalFolderId, totalFolderName } from '@/util/constants';
-import { FoldersContext, LinkListContext } from '@/context/createContext.';
+import { FoldersContext } from '@/context/createContext.';
import FolderList from '@/components/common/FolderList';
import { useRouter } from 'next/router';
diff --git a/components/pages/sign/InputGroup.tsx b/components/pages/sign/InputGroup.tsx
new file mode 100644
index 000000000..92fa00455
--- /dev/null
+++ b/components/pages/sign/InputGroup.tsx
@@ -0,0 +1,38 @@
+import { useFormContext } from 'react-hook-form';
+import styles from '@/styles/sign.module.css';
+import { useState } from 'react';
+
+const InputGroup = ({ info }: any) => {
+ const {
+ register,
+ trigger,
+ formState: { errors },
+ } = useFormContext();
+ const [showText, setShowText] = useState(() => (info.id !== 'email' ? false : true));
+ const type = info.id === 'email' ? info.type : showText ? 'text' : info.type;
+ const eyeClassName = `${styles.iconEye} ${showText ? styles.on : ''}`;
+ const inputClassName = `${styles.input} ${errors[info.id]?.message ? styles.error : ''}`;
+
+ return (
+ <>
+
+
+ await trigger(info.id) })}
+ placeholder={info.placeholder}
+ className={inputClassName}
+ />
+ {info.type === 'password' && (
+ setShowText(!showText)} />
+ )}
+
+ {errors?.[info.id] && {errors[info.id]?.message as string}
}
+ >
+ );
+};
+
+export default InputGroup;
diff --git a/constants/footerItems.ts b/constants/footerItems.ts
index 90b5ebbe8..c1d8b0266 100644
--- a/constants/footerItems.ts
+++ b/constants/footerItems.ts
@@ -1,4 +1,4 @@
-import { FooterItem } from "@/types/types";
+import type { FooterItem } from "@/types/types";
export const SNS_ITEMS: FooterItem[] = [
{
diff --git a/constants/sign.ts b/constants/sign.ts
new file mode 100644
index 000000000..68dfbb7ca
--- /dev/null
+++ b/constants/sign.ts
@@ -0,0 +1,69 @@
+export const ERROR_MESSAGE = {
+ email: {
+ pattern: '올바른 이메일 주소가 아닙니다.',
+ required: '이메일을 입력해 주세요.',
+ checkRight: '이메일을 확인해 주세요.',
+ },
+ password: {
+ pattern: '비밀번호는 영문, 숫자 조합 8자 이상 입력해 주세요.',
+ required: '비밀번호를 입력해 주세요',
+ checkSame: '비밀번호가 일치하지 않아요',
+ checkRight: '비밀번호를 확인해 주세요.',
+ },
+};
+
+export const INPUT_INFO = {
+ email: {
+ id: 'email',
+ type: 'email',
+ label: '이메일',
+ placeholder: '이메일를 입력해 주세요.',
+ validation: {
+ pattern: {
+ value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
+ message: ERROR_MESSAGE.email.pattern,
+ },
+ required: ERROR_MESSAGE.email.required,
+ },
+ },
+ password: {
+ signIn: {
+ id: 'password',
+ type: 'password',
+ label: '비밀번호',
+ placeholder: '비밀번호를 입력해 주세요.',
+ validation: {
+ required: ERROR_MESSAGE.password.required,
+ },
+ },
+ signUp: {
+ id: 'password',
+ type: 'password',
+ label: '비밀번호 ',
+ placeholder: '비밀번호를 입력해 주세요.',
+ validation: {
+ pattern: {
+ value: /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,}$/,
+ message: ERROR_MESSAGE.password.pattern,
+ },
+ required: ERROR_MESSAGE.password.required,
+ },
+ },
+ },
+ passwordCheck: {
+ id: 'passwordCheck',
+ type: 'password',
+ label: '비밀번호 확인',
+ placeholder: '비밀번호와 일치하는 값을 입력해주세요.',
+ validation: {
+ validate: (value: any, formValues: any) => {
+ if (value.length === 0 && formValues.password.length === 0) return ERROR_MESSAGE.password.required;
+ return value !== formValues.password && ERROR_MESSAGE.password.checkSame;
+ },
+ pattern: {
+ value: /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,}$/,
+ message: ERROR_MESSAGE.password.pattern,
+ },
+ },
+ },
+};
diff --git a/context/createContext..tsx b/context/createContext..tsx
index 2369eb83c..7d24ef58c 100644
--- a/context/createContext..tsx
+++ b/context/createContext..tsx
@@ -1,5 +1,5 @@
import { createContext } from 'react';
-import { Folder, LinkTypes } from '@/types/types';
+import type { Folder, LinkTypes } from '@/types/types';
export const FoldersContext = createContext([]);
diff --git a/context/sampleFolderContext.tsx b/context/sampleFolderContext.tsx
index 8e559a37e..938270ce6 100644
--- a/context/sampleFolderContext.tsx
+++ b/context/sampleFolderContext.tsx
@@ -1,6 +1,6 @@
import { PropsWithChildren, createContext, useEffect, useState } from 'react';
import { getSampleFolder } from '@/api/api';
-import { LinkTypes } from '@/types/types';
+import type { LinkTypes } from '@/types/types';
interface SampleFolderContext {
folderName: string;
diff --git a/package-lock.json b/package-lock.json
index 920662371..bf01064d5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"react": "^18",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18",
+ "react-hook-form": "^7.51.2",
"react-router-dom": "^6.22.3",
"styled-components": "^6.1.8",
"uuid": "^9.0.1"
@@ -3317,6 +3318,21 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-hook-form": {
+ "version": "7.51.2",
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.2.tgz",
+ "integrity": "sha512-y++lwaWjtzDt/XNnyGDQy6goHskFualmDlf+jzEZvjvz6KWDf7EboL7pUvRCzPTJd0EOPpdekYaQLEvvG6m6HA==",
+ "engines": {
+ "node": ">=12.22.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-hook-form"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
diff --git a/package.json b/package.json
index 312ec13be..c2c18762d 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"react": "^18",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18",
+ "react-hook-form": "^7.51.2",
"react-router-dom": "^6.22.3",
"styled-components": "^6.1.8",
"uuid": "^9.0.1"
diff --git a/pages/folder/[id].tsx b/pages/folder/[id].tsx
index ea2f46f71..a8a746b37 100644
--- a/pages/folder/[id].tsx
+++ b/pages/folder/[id].tsx
@@ -2,24 +2,26 @@ import { getUserFolders, getUserLinks } from '@/api/api';
import AddLinkArea from '@/components/pages/folder/AddLinkArea';
import FolderSection from '@/components/pages/folder/FolderSection';
import { FoldersContext, LinkListContext } from '@/context/createContext.';
-import { Folder, LinkTypes } from '@/types/types';
+import type { Folder, LinkTypes } from '@/types/types';
+import { GetServerSideProps } from 'next';
-//? context의 타입은 뭔가요?
-export async function getServerSideProps(context: any) {
- const folderId = context.params['id'];
- let folderList, links;
+export const getServerSideProps: GetServerSideProps = async context => {
+ // TODO: id는 string, string[], undefined가 될 수 있다.
+ const { id } = context.query;
+
+ if (!id) return { notFound: true };
try {
- folderList = await getUserFolders();
- links = await getUserLinks(folderId);
+ const folderList = await getUserFolders();
+ const links = await getUserLinks(id as string);
+
+ return { props: { folderList, links } };
} catch {
return {
notFound: true,
};
}
-
- return { props: { folderList, links } };
-}
+};
const FolderPage = ({ folderList, links }: { folderList: Folder[]; links: LinkTypes[] }) => {
return (
diff --git a/pages/folder/index.tsx b/pages/folder/index.tsx
index 718ae7ea3..6ae05d90c 100644
--- a/pages/folder/index.tsx
+++ b/pages/folder/index.tsx
@@ -2,22 +2,20 @@ import { getUserFolders, getUserLinks } from '@/api/api';
import AddLinkArea from '@/components/pages/folder/AddLinkArea';
import FolderSection from '@/components/pages/folder/FolderSection';
import { FoldersContext, LinkListContext } from '@/context/createContext.';
-import { Folder, LinkTypes } from '@/types/types';
+import type { Folder, LinkTypes } from '@/types/types';
import { totalFolderId } from '@/util/constants';
export async function getServerSideProps() {
- let folderList, links;
-
try {
- folderList = await getUserFolders();
- links = await getUserLinks(totalFolderId);
+ const folderList = await getUserFolders();
+ const links = await getUserLinks(totalFolderId);
+
+ return { props: { folderList, links } };
} catch {
return {
notFound: true,
};
}
-
- return { props: { folderList, links } };
}
const FolderPage = ({ folderList, links }: { folderList: Folder[]; links: LinkTypes[] }) => {
diff --git a/pages/shared.tsx b/pages/shared.tsx
index 4a669cf8a..351116154 100644
--- a/pages/shared.tsx
+++ b/pages/shared.tsx
@@ -2,7 +2,7 @@ import UserFolderNameArea from '@/components/pages/shared/UserFolderNameArea';
import SharedSection from '@/components/pages/shared/SharedSection';
import { createContext, useEffect, useState } from 'react';
import { getSampleFolder } from '@/api/api';
-import { LinkTypes } from '@/types/types';
+import type { LinkTypes } from '@/types/types';
import { LinkListContext } from '@/context/createContext.';
export async function getStaticProps() {
diff --git a/pages/signin.tsx b/pages/signin.tsx
new file mode 100644
index 000000000..db8f693ce
--- /dev/null
+++ b/pages/signin.tsx
@@ -0,0 +1,56 @@
+import InputGroup from '@/components/pages/sign/InputGroup';
+import { INPUT_INFO } from '@/constants/sign';
+import styles from '@/styles/sign.module.css';
+import Image from 'next/image';
+import Link from 'next/link';
+import { FormProvider, useForm } from 'react-hook-form';
+
+export default function SignIn() {
+ const methods = useForm();
+
+ const onSubmit = (data: any) => {
+ console.log(data);
+ };
+
+ return (
+
+ );
+}
diff --git a/pages/signup.tsx b/pages/signup.tsx
new file mode 100644
index 000000000..d1f74c7fa
--- /dev/null
+++ b/pages/signup.tsx
@@ -0,0 +1,58 @@
+import InputGroup from '@/components/pages/sign/InputGroup';
+import { INPUT_INFO } from '@/constants/sign';
+import styles from '@/styles/sign.module.css';
+import Image from 'next/image';
+import Link from 'next/link';
+import { FormProvider, useForm } from 'react-hook-form';
+
+export default function SignIn() {
+ const methods = useForm();
+
+ const onSubmit = (data: any) => {
+ console.log(data);
+ };
+
+ return (
+
+ );
+}
diff --git a/styles/sign.module.css b/styles/sign.module.css
new file mode 100644
index 000000000..83677677f
--- /dev/null
+++ b/styles/sign.module.css
@@ -0,0 +1,174 @@
+/* body {
+ background: var(--color-gray-200);
+} */
+
+.container {
+ margin: 0 3.2rem;
+}
+
+.headerArea {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1.6rem;
+ margin: 23.8rem 0 3rem;
+}
+
+.goToMain {
+ width: 21.058rem;
+ height: 3.8rem;
+}
+
+.logo {
+ width: 100%;
+ height: 100%;
+}
+
+.question {
+ display: flex;
+ align-items: flex-start;
+ gap: 0.8rem;
+ font-size: 1.6rem;
+ font-weight: 400;
+ line-height: 2.4rem;
+}
+
+.link {
+ position: relative;
+ line-height: 1.1;
+ font-weight: 600;
+ color: var(--color-primary);
+}
+
+.link::after {
+ content: '';
+ position: absolute;
+ bottom: -0.3rem;
+ display: block;
+ width: 100%;
+ border-bottom: 0.1rem solid var(--color-primary);
+}
+
+.formArea {
+ display: flex;
+ flex-direction: column;
+ margin: 0 auto 3.2rem;
+ max-width: 40rem;
+}
+
+.label {
+ margin: 2.4rem 0 1.2rem;
+ line-height: 1.7rem;
+ font-size: 1.4rem;
+ font-weight: 400;
+}
+
+.label:first-of-type {
+ margin-top: 0;
+}
+
+.inputGroup {
+ position: relative;
+}
+
+.input {
+ padding: 1.8rem 1.5rem;
+ width: 100%;
+ height: 6rem;
+ border: 0.1rem solid var(--color-gray-400);
+ border-radius: 0.8rem;
+ font-size: 1.6rem;
+ font-weight: 400;
+}
+
+.input:focus {
+ outline: none;
+ border: 0.1rem solid var(--color-primary);
+}
+
+.input.error {
+ border: 1px solid var(--color-red);
+}
+
+.messageError {
+ margin-top: 0.6rem;
+ font-size: 1.4rem;
+ font-weight: 400;
+ color: var(--color-red);
+}
+
+.hidden {
+ display: none;
+}
+
+.iconEye {
+ position: absolute;
+ top: 50%;
+ right: 1.5rem;
+ width: 1.6rem;
+ height: 1.6rem;
+ transform: translateY(-50%);
+ background: url('../public/icons/eye-off.svg') no-repeat center bottom/contain;
+}
+
+.iconEye.on {
+ background: url('../public/icons/eye-on.svg') no-repeat center bottom/contain;
+}
+
+.button {
+ padding: 1.6rem 2rem;
+ border-radius: 0.8rem;
+ background: linear-gradient(91deg, #6d6afe 0.12%, #6ae3fe 101.84%);
+ line-height: 2.1rem;
+ font-size: 1.8rem;
+ font-weight: 600;
+ color: var(--color-gray-100);
+ text-align: center;
+ margin-top: 3rem;
+}
+
+.snsButtonGroup {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 0 auto;
+ padding: 1.2rem 2.4rem;
+ max-width: 40rem;
+ height: 6.6rem;
+ border: 0.1rem solid var(--color-gray-400);
+ background: var(--color-gray-300);
+}
+
+.snsButtonGroup .title {
+ flex-shrink: 0;
+ font-size: 1.4rem;
+ font-weight: 400;
+ color: #000;
+}
+
+.buttonList {
+ display: flex;
+ gap: 1.6rem;
+}
+
+.buttonItem {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 4.2rem;
+ height: 4.2rem;
+ border-radius: 50%;
+}
+
+.buttonItem.google {
+ border: 0.1rem solid #d3d4dd;
+ background: var(--color-white);
+}
+
+.buttonItem.kakao {
+ background: var(--color-yellow);
+}
+
+.buttonItem a {
+ display: flex;
+}