Skip to content

Commit dbec029

Browse files
committed
[feat] 글쓰기 기능
1 parent 76c6c2b commit dbec029

File tree

10 files changed

+129
-45
lines changed

10 files changed

+129
-45
lines changed

src/app/community/write/page.tsx

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
'use client';
22

3-
import Tag from '@/domains/community/components/tag/Tag';
4-
import Category from '@/domains/community/write/Category';
53
import TagModal from '@/domains/community/write/cocktail-tag/TagModal';
6-
import CompleteBtn from '@/domains/community/write/CompleteBtn';
7-
import FormTitle from '@/domains/community/write/FormTitle';
8-
import ImageSection from '@/domains/community/write/image-upload/ImageSection';
9-
import WriteForm from '@/domains/community/write/WriteForm';
4+
import WriteSection from '@/domains/community/write/WriteSection';
105
import StarBg from '@/domains/shared/components/star-bg/StarBg';
116
import { useState } from 'react';
127

@@ -17,16 +12,7 @@ function Page() {
1712
<div className="w-full mb-20 flex relative">
1813
<StarBg className="w-full h-32 absolute"></StarBg>
1914
<div className="page-layout max-w-824 flex-1 z-5">
20-
<CompleteBtn />
21-
<section>
22-
<FormTitle />
23-
<Category />
24-
<WriteForm />
25-
</section>
26-
<ImageSection />
27-
<section className="mt-8">
28-
<Tag use="write" onClick={() => setIsOpen(true)} />
29-
</section>
15+
<WriteSection setIsOpen={setIsOpen} />
3016
</div>
3117
{isOpen && <TagModal isOpen={isOpen} setIsOpen={setIsOpen} />}
3218
</div>

src/domains/community/write/Category.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
import SelectBox from '@/shared/components/select-box/SelectBox';
2+
import { Dispatch, SetStateAction } from 'react';
3+
import { FormType } from './WriteSection';
24

3-
function Category() {
5+
type Props = {
6+
setFormData: Dispatch<SetStateAction<FormType>>;
7+
};
8+
9+
function Category({ setFormData }: Props) {
410
return (
511
<div className="w-full h-[38px] flex items-center justify-end mt-10">
6-
<SelectBox option={['레시피', '팁', '질문', '자유']} title="카테고리" use="write" />
12+
<SelectBox
13+
option={['레시피', '팁', '질문', '자유']}
14+
title="카테고리"
15+
use="write"
16+
onChange={(categoryRef) =>
17+
setFormData((prev) => ({
18+
...prev,
19+
categoryName: categoryRef,
20+
}))
21+
}
22+
/>
723
</div>
824
);
925
}

src/domains/community/write/FormTitle.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
function FormTitle() {
1+
import { Dispatch, SetStateAction } from 'react';
2+
import { FormType } from './WriteSection';
3+
4+
type Props = {
5+
setFormData: Dispatch<SetStateAction<FormType>>;
6+
};
7+
8+
function FormTitle({ setFormData }: Props) {
29
return (
310
<div className="w-full h-[69px] relative border-b-1 border-gray mt-5 flex items-end pb-2">
411
<label id="title-label" htmlFor="writingTitle" className="sr-only">
@@ -11,6 +18,12 @@ function FormTitle() {
1118
id="writingTitle"
1219
maxLength={20}
1320
aria-describedby="title-count"
21+
onChange={(e) => {
22+
setFormData((prev) => ({
23+
...prev,
24+
title: e.target.value,
25+
}));
26+
}}
1427
/>
1528
<span id="title-count" aria-live="polite" className="text-gray ">
1629
0/20

src/domains/community/write/WriteForm.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
function WriteForm() {
1+
import { Dispatch, SetStateAction } from 'react';
2+
import { FormType } from './WriteSection';
3+
4+
type Props = {
5+
setFormData: Dispatch<SetStateAction<FormType>>;
6+
};
7+
8+
function WriteForm({ setFormData }: Props) {
29
return (
3-
<form className="mt-5">
10+
<div className="mt-5">
411
<label htmlFor="content" className="sr-only">
512
글 내용
613
</label>
@@ -11,8 +18,14 @@ function WriteForm() {
1118
aria-multiline="true"
1219
tabIndex={0}
1320
className="w-full min-h-80 max-h-120 overflow-y-auto no-scrollbar bg-white rounded-3xl focus:outline-none py-7 px-5 text-primary"
21+
onInput={(e) => {
22+
setFormData((prev) => ({
23+
...prev,
24+
content: (e.target as HTMLDivElement).innerText,
25+
}));
26+
}}
1427
></div>
15-
</form>
28+
</div>
1629
);
1730
}
1831

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { useState } from 'react';
2+
import Category from './Category';
3+
import FormTitle from './FormTitle';
4+
import WriteForm from './WriteForm';
5+
import CompleteBtn from './CompleteBtn';
6+
import ImageSection from './image-upload/ImageSection';
7+
import Tag from '../components/tag/Tag';
8+
import { getApi } from '@/app/api/config/appConfig';
9+
10+
export type FormType = {
11+
categoryName: string;
12+
title: string;
13+
content: string;
14+
imageUrls: string[];
15+
tags: string[];
16+
};
17+
18+
type Props = {
19+
setIsOpen: (value: boolean) => void;
20+
};
21+
22+
function WriteSection({ setIsOpen }: Props) {
23+
const [formData, setFormData] = useState<FormType>({
24+
categoryName: '',
25+
title: '',
26+
content: '',
27+
imageUrls: [],
28+
tags: [],
29+
});
30+
31+
const handleSubmit = async (e: React.FormEvent) => {
32+
e.preventDefault();
33+
34+
try {
35+
const res = await fetch(`${getApi}/posts`, {
36+
method: 'POST',
37+
headers: {
38+
'Content-Type': 'application/json; charset=UTF-8',
39+
},
40+
body: JSON.stringify({
41+
title: formData.title,
42+
content: formData.content,
43+
categoryName: formData.categoryName,
44+
}),
45+
});
46+
47+
console.log('▶ 요청 보낸 후 status:', res.status);
48+
const text = await res.text();
49+
console.log('▶ 응답 텍스트:', text);
50+
if (res.ok) {
51+
console.log('글작성 성공', formData);
52+
}
53+
} catch (err) {
54+
console.error('글작성 폼 작성 에러', err);
55+
return;
56+
}
57+
};
58+
59+
return (
60+
<form onSubmit={handleSubmit}>
61+
<CompleteBtn />
62+
<section>
63+
<FormTitle setFormData={setFormData} />
64+
<Category setFormData={setFormData} />
65+
<WriteForm setFormData={setFormData} />
66+
</section>
67+
<ImageSection />
68+
<section className="mt-8">
69+
<Tag use="write" onClick={() => setIsOpen(true)} />
70+
</section>
71+
</form>
72+
);
73+
}
74+
75+
export default WriteSection;

src/domains/community/write/cocktail-tag/Tag.tsx

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/domains/community/write/image-upload/ImageSection.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,6 @@ function ImageSection() {
66
<section className="mt-5 sm:grid md:grid-cols-7 sm:grid-cols-5 sm:place-items-center flex overflow-y-scroll no-scrollbar gap-5 whitespace-nowrap py-5 w-full max-w-full">
77
<DragandClick />
88
<UploadedImage />
9-
<UploadedImage />
10-
<UploadedImage />
11-
<UploadedImage />
12-
<UploadedImage />
13-
<UploadedImage />
14-
<UploadedImage />
15-
<UploadedImage />
169
</section>
1710
);
1811
}

src/domains/community/write/image-upload/UploadedImage.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
'use client';
22

33
import Image from 'next/image';
4-
import { useState } from 'react';
54
import DeleteIcon from '@/shared/assets/icons/close_20.svg';
65

76
function UploadedImage() {
8-
const [hovered, setHovered] = useState(false);
9-
107
return (
11-
<figure
12-
className="border-3 border-gray-light w-[80px] h-[80px] rounded-xl relative shrink-0"
13-
onMouseEnter={() => setHovered(true)}
14-
onMouseLeave={() => setHovered(false)}
15-
>
8+
<figure className="border-3 border-gray-light w-[80px] h-[80px] rounded-xl relative shrink-0">
169
<Image
1710
src={''}
1811
alt="칵테일 이미지 예시"

src/shared/components/Input-box/Input.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22
import tw from '@/shared/utills/tw';
33
import { cva } from 'class-variance-authority';
4-
import { HTMLInputTypeAttribute, Ref } from 'react';
4+
import { ChangeEvent, HTMLInputTypeAttribute, Ref } from 'react';
55
import Search from '@/shared/assets/icons/search_32.svg';
66
import Button from '../button/Button';
77
// select나올떄 자연스러운 처리 화살표 로테이트 [x]
@@ -18,7 +18,7 @@ interface Props {
1818
size?: 'default' | 'lg';
1919
variant?: 'default' | 'search' | 'comment';
2020
className?: string;
21-
onChange?: () => void;
21+
onChange?: (() => void) | ((e: ChangeEvent<HTMLInputElement>) => void);
2222
id: string;
2323
}
2424

src/shared/components/select-box/SelectBox.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function SelectBox({ id, groupKey, ref, option, title, onChange, use }: Props) {
5454
else
5555
setIsOpen((prev) => {
5656
const next = !prev;
57-
console.log('TOGGLE BTN CLICK:', { prev, next });
57+
// console.log('TOGGLE BTN CLICK:', { prev, next });
5858
return next;
5959
});
6060
};

0 commit comments

Comments
 (0)