React를 활용한 공지사항 작성
https://joygotohome.tistory.com/124
'공지사항 목록 조회' -> '공지사항 작성'으로 이어지는 글입니다.
1. create 훅 만들기
\features\notice\model\use-notice-create.ts
import { useNavigate } from 'react-router-dom'
import { useMutation } from '@tanstack/react-query';
import { Form } from 'antd';
import { createNotice } from '../api/notice-api';
import { getCompanyId } from '@/shared/hooks/use-company-info';
import type { NoticeCreateRequest, NoticeCreateFormValues } from './types';
export const useNoticeCreate = () => {
const navigate = useNavigate();
/**
* [form] : 배열로 받는 이유 → useForm이 [FormInstance, ...] 형태로 반환하기 때문
* form 객체로 값 가져오기, 초기화, 유효성 검사 등을 제어
*
* useQuery → 데이터 가져올 때 (GET)
* useMutation → 데이터 변경할 때 (POST, PUT, DELETE)
*
* isPending : API 호출 중인지 여부를 알려주는 값
* submitNotice() 호출 전 → isPending = false
* submitNotice() 호출 중 → isPending = true (서버 응답 기다리는 중)
* API 완료 후 → isPending = false
*/
// Form.useForm : AntD Form 상태 관리 훅
const [form] = Form.useForm<NoticeCreateFormValues>();
// useMutation({ 원래이름: 바꾼이름, isPending }) React Query 에서 제공하는 훅
const { mutate: submitNotice, isPending } = useMutation({
// mutationFn: (data: type설정값) => API함수이름(data),
mutationFn: (data: NoticeCreateRequest) => createNotice(data), // data : 나중에 mutate() 호출할 때 넘기는 값
// 작성 성공 시 목록 페이지로 이동
onSuccess: () => { //onSuccess : API 호출이 성공했을 때 실행되는 콜백 함수
navigate('/notice');
}
});
//제출
const handleSubmit = () => {
form.validateFields().then((values) => { // 폼 유효성 검사
submitNotice({ //submitNotice : mutate를 이름 바꾼것
...values, // 폼에서 입력한 값들을 펼쳐서 넣음 (title, content 등)
companyId: getCompanyId(),
startDate: values.startDate?.format('YYYY-MM-DD') ?? '',
endDate: values.endDate?.format('YYYY-MM-DD') ?? '',
})
})
};
//취소: 공지사항으로 이동
const handleCancel = () => {
navigate('/notice');
};
return {
form, // 폼 인스턴스
isPending, // 로딩 상태
handleSubmit, // 제출
handleCancel, // 취소
};
};
2. API 연결 추가
\features\notice\api\notice-api.ts
import apiClient from '@/shared/api/api-client';
import { getCompanyId } from '@/shared/hooks/use-company-info';
import type { NoticeCreateRequest, NoticeDetail, NoticeListItem, PageResponse } from '../model/types';
const BASE_URL = '/notices';
// 목록 조회 - getData 사용 (data만 바로 반환)
export const getNoticeList = async (page = 0, size = 20): Promise<PageResponse<NoticeListItem>> => {
const companyId = getCompanyId();
return await apiClient.getData(`${BASE_URL}/company/${companyId}`, { params: { page, size } });
};
// 작성
export const createNotice = async (data: NoticeCreateRequest): Promise<{ id: number }> => {
return await apiClient.postData(`${BASE_URL}`, data);
};
백엔드 컨트롤러를 참고하여 주소를 확인해서 만듭니다.
3. TYPE 추가
\features\notice\model\types.ts
import type { Dayjs } from 'dayjs';
export interface NoticeCreateRequest {
companyId?: number | null;
title: string;
content: string;
targetType?: string | null;
displayType?: string | null;
startDate: string | null;
endDate: string | null;
}
export interface NoticeCreateFormValues {
title: string;
content: string;
targetType: string;
displayType: string;
startDate: Dayjs; // 폼에서는 dayjs 객체
endDate: Dayjs;
}
백엔드 DTO를 참고하여 타입을 지정합니다.
만든 값을 넘길때 날짜 타입을 정확히 하여 날리기 위해서 별로도 FormValues를 제작하였습니다.
4. form그리기
\features\notice\ui\notice-create.tsx
import { Form, Input, Select, DatePicker } from 'antd';
import { useNoticeCreate } from '../../model/use-notice-create';
import { WriteContainer, WriteForm, ButtonFlex, CancelButton, SubmitButton,} from './notice-create.styles';
export const NoticeCreate = () => {
const { form, isPending, handleSubmit, handleCancel } = useNoticeCreate();
return (
<WriteContainer>
<WriteForm form={form}>
<Form.Item name="targetType" label="공지 대상" rules={[{ required: true, message: '공지 대상을 선택해주세요' }]}>
<Select>
<Select.Option value="ALL">전체</Select.Option>
<Select.Option value="SYSTEM">시스템 업데이트</Select.Option>
</Select>
</Form.Item>
<Form.Item name="displayType" label="노출 방식" rules={[{ required: true, message: '노출 방식을 선택해주세요' }]}>
<Select>
<Select.Option value="BANNER">배너</Select.Option>
<Select.Option value="POPUP">팝업</Select.Option>
</Select>
</Form.Item>
<Form.Item name="startDate" label="시작일" rules={[{ required: true, message: '시작일을 선택해주세요' }]}>
<DatePicker format="YYYY-MM-DD" />
</Form.Item>
<Form.Item name="endDate" label="종료일" rules={[{ required: true, message: '종료일을 선택해주세요' }]}>
<DatePicker format="YYYY-MM-DD" />
</Form.Item>
<Form.Item name="title" label="제목" rules={[{ required: true, message: '제목을 입력해주세요' }]}>
<Input />
</Form.Item>
<Form.Item name="content" label="내용" rules={[{ required: true, message: '내용을 입력해주세요' }]}>
<Input.TextArea rows={4} />
</Form.Item>
<ButtonFlex>
<CancelButton onClick={handleCancel}>취소</CancelButton>
<SubmitButton type="primary" loading={isPending} onClick={handleSubmit}>작성</SubmitButton>
</ButtonFlex>
</WriteForm>
</WriteContainer>
);
};
기본 폼을 지정합니다.
\features\notice\ui\notice-create.styles.ts
import styled from 'styled-components';
import { Form, Button, Flex } from 'antd';
// 전체 폼을 감싸는 컨테이너
// 세로 방향으로 필드들 배치
export const WriteContainer = styled(Flex)`
flex-direction: column;
padding: 1.5rem;
background-color: white;
border-radius: 0.5rem;
`;
// Form 을 styled 로 감쌀 때 as typeof Form 필요
// → Form 의 타입 정보가 유지되어야 form={form} 같은 props 오류 안남
export const WriteForm = styled(Form)`
width: 100%;
` as typeof Form;
// 제목, 내용 입력 필드 너비 100%
export const WriteInput = styled.input`
width: 100%;
`;
// 버튼 영역
export const ButtonFlex = styled(Flex)`
justify-content: flex-end;
gap: 0.5rem;
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid #d9d9d9;
`;
export const CancelButton = styled(Button)`
min-width: 5rem;
`;
export const SubmitButton = styled(Button)`
min-width: 5rem;
`;
스타일은 나중에 다시 잡기로 하고, 일단 틀만 만들었습니다.
5. page 연결하기
\features\notice\index.ts
export type { NoticeCreateRequest, NoticeDetail, NoticeListItem, PageResponse } from './model/types';
export { NoticeList } from './ui/notice-list';
export { NoticeCreate } from './ui/notice-create';
index를 잡아줍니다.
\pages\notice\ui\notice-create-page.tsx
import { NoticeCreate } from '@/features/notice'
export const NoticeCreatePage = () => {
return (
<NoticeCreate />
)
}
페이지를 연결해줍니다.

공지사항 작성 화면 기본 틀이 잡혔습니다.
'React' 카테고리의 다른 글
| [React] 공지사항 게시판 만들기(4): update 공지사항 수정 (0) | 2026.03.24 |
|---|---|
| [React] 공지사항 게시판 만들기(3): detail 공지사항 상세페이지 조회 (0) | 2026.03.23 |
| [React] 공지사항 게시판 만들기(1): list 공지사항 목록 조회 (0) | 2026.03.23 |
| [React] 프로젝트 생성방법 vite, CRA (0) | 2026.01.07 |
| [React] NextJS 새로운 페이지 생성 및 연결 방법 (0) | 2026.01.07 |