) {
+ const _context = useContext(context);
+
+ if (!_context) {
+ throw new Error('Context는 반드시 Provider로 감싸져야 합니다.');
+ }
+
+ return _context;
+}
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 9af623c..034a1ae 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -1,15 +1,27 @@
+import type { NextPage } from 'next';
import type { AppProps } from 'next/app';
+import type { ReactElement, ReactNode } from 'react';
import { Hydrate, QueryProvider } from '@/providers';
import 'bootstrap/dist/css/bootstrap.min.css';
import { montserrat, pretendard } from '@/styles/fonts';
import '@/styles/globals.css';
-export default function App({ Component, pageProps }: AppProps) {
+export type NextPageWithLayout = NextPage
& {
+ getLayout?: (page: ReactElement) => ReactNode;
+};
+
+type AppPropsWithLayout = AppProps & {
+ Component: NextPageWithLayout;
+};
+
+export default function App({ Component, pageProps }: AppPropsWithLayout) {
+ const getLayout = Component.getLayout || ((page) => page);
+
return (
-
+ {getLayout()}
diff --git a/src/pages/user/[id]/index.tsx b/src/pages/user/[id]/index.tsx
index b277996..fc9b16f 100644
--- a/src/pages/user/[id]/index.tsx
+++ b/src/pages/user/[id]/index.tsx
@@ -1,14 +1,44 @@
-import { Avatar, Button, Icon } from '@/components/shared';
+import { type ReactElement, useState } from 'react';
+import { useSafeContext } from '@/hooks';
+import type { NextPageWithLayout } from '@/pages/_app';
+import { ModalContext, ModalProvider } from '@/providers';
+import { Avatar, Button, Icon, Tooltip } from '@/components/shared';
import { Drawer } from '@/components/shared/Drawer';
-import { CameraRoll } from '@/components/user/CameraRoll';
-import { useToggle } from '@/hooks/useToggle';
+import { AddMenu } from '@/components/user';
+import { CameraRoll, FilmAddModal, FilmSelectModal, FilmTitleModal, ProfileModal } from '@/components/user';
-export default function User() {
+export interface Profile {
+ profileImage: string;
+ nickname: string;
+ description: string;
+}
+
+const User: NextPageWithLayout = () => {
+ const { status, dispatch } = useSafeContext(ModalContext);
const {
- status: isDrawerOpen,
- setOn: openDrawer,
- setOff: closeDrawer,
- } = useToggle();
+ isDrawerOpen,
+ isAddMenuOpen,
+ isProfileModalOpen,
+ isFilmAddModalOpen,
+ isFilmSelectModalOpen,
+ isFilmTitleModalOpen,
+ } = status;
+ const [editingTitle, setEditingTitle] = useState('');
+ const [userInfo, setUserInfo] = useState({
+ profileImage: '/images/profile.png',
+ nickname: '',
+ description: '',
+ });
+
+ const handleEditTitle = (title: string) => {
+ setEditingTitle(title);
+ dispatch({ type: 'OPEN_FILM_TITLE_MODAL' });
+ };
+
+ const handleEditProfile = (info: Profile) => {
+ setUserInfo(info);
+ dispatch({ type: 'OPEN_PROFILE_MODAL' });
+ };
return (
@@ -17,27 +47,60 @@ export default function User() {
nickname='Jichoi'
displayMeta
className='tw-mx-5'
+ onEditProfile={handleEditProfile}
/>
{/* {TODO: 방명록 기능 추가할 때 변경} */}
방명록 기능이 추가될 공간입니다 ㅎ
-
-
-
+
+
+
-
);
-}
+};
+
+User.getLayout = function getLayout(page: ReactElement) {
+ return {page};
+};
+
+export default User;
diff --git a/src/pages/user/[id]/item/add/index.tsx b/src/pages/user/[id]/item/add/index.tsx
index 542c7dd..b947377 100644
--- a/src/pages/user/[id]/item/add/index.tsx
+++ b/src/pages/user/[id]/item/add/index.tsx
@@ -1,17 +1,12 @@
import { useRouter } from 'next/router';
import { useRef, useState } from 'react';
import { convertImageToBase64 } from '@/utils';
-import {
- Icon,
- ImageFrame,
- Input,
- TextButton,
- Textarea,
-} from '@/components/shared';
+import { Icon, ImageFrame, Input, TextButton, Textarea } from '@/components/shared';
+import { isString } from '@/utils/type-util';
export default function AddPage() {
const router = useRouter();
- const { id } = router.query;
+ const { id, title } = router.query;
const inputRef = useRef(null);
const [image, setImage] = useState(null);
@@ -70,7 +65,7 @@ export default function AddPage() {
{/** 본문 입력 영역 */}
-
+
{/* */}
diff --git a/src/providers/ModalProvider.tsx b/src/providers/ModalProvider.tsx
new file mode 100644
index 0000000..4bc77e3
--- /dev/null
+++ b/src/providers/ModalProvider.tsx
@@ -0,0 +1,16 @@
+import { type Dispatch, type PropsWithChildren, createContext, useMemo } from 'react';
+import { Action, State, useModals } from '@/components/user/hooks';
+
+type ModalContextType = {
+ status: State;
+ dispatch: Dispatch;
+};
+
+export const ModalContext = createContext(null);
+
+export function ModalProvider({ children }: PropsWithChildren) {
+ const { status, dispatch } = useModals();
+ const contextValue = useMemo(() => ({ status, dispatch }), [status, dispatch]);
+
+ return {children};
+}
diff --git a/src/providers/index.ts b/src/providers/index.ts
index 9a50fde..d2fffd3 100644
--- a/src/providers/index.ts
+++ b/src/providers/index.ts
@@ -1,2 +1,3 @@
export * from './Hydrate';
export * from './QueryProvider';
+export * from './ModalProvider';
diff --git a/src/utils/type-util.ts b/src/utils/type-util.ts
new file mode 100644
index 0000000..9b057cf
--- /dev/null
+++ b/src/utils/type-util.ts
@@ -0,0 +1,3 @@
+export const isString = (value: unknown): value is string => {
+ return typeof value === 'string';
+};
diff --git a/tailwind.config.js b/tailwind.config.js
index 4402217..834ff66 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -17,7 +17,8 @@ module.exports = {
},
colors: {
primary: '#202020',
- nudge: '#19F1CA',
+ nudge: '#FACB2E',
+ blue: '#0066FF',
danger: '#F40000',
yellow: '#FACB2E',
grayscale: {