Posting
명상이 완료된 뒤 글을 발행하는 페이지입니다.
글 작성을 위해 다음과 같은 API 를 사용합니다.
API
사용자의 토큰과 formData 를 받아 서버로 글 작성 요청을 보내는 함수입니다.
import axios from 'axios'; import { API_BASE_URL } from '@/constants/Api'; const postCreateNewPost = async (token: string, formData: FormData) => { const response = await axios.post(`${API_BASE_URL}/posts/create`, formData, { headers: { Authorization: `bearer ${token}` } }); return response; }; export default postCreateNewPost;
성공시 발행한 포스트에 대한 정보가 반환됩니다.

Posting.tsx
유효한 접근 확인
Posting
는 Meditation
페이지를 통해서만 접근이 가능합니다. 따라서 Meditation 에서 다음과 같이 유효한 접근인지에 대한 정보와, 사용자가 글을 쓸 명상 채널의 정보를 가져옵니다.const Posting = () => { const location = useLocation(); const channelId = location.state.channelId; const validateAccess = location.state.validate; }
유효하지 않은 접근의 경우 경고창을 띄우고 다른 페이지로 이동시킵니다.
// 추후 로직 추가
유저 토큰 가져오기
유저 정보는 전역으로 관리합니다. 글을 쓸 때 사용자의 토큰 정보가 필요하기 때문에 다음과 같이 가져옵니다.
Posting 페이지에 접근 하기 전의 인증은 Router 에서 처리해줌으로써 신경쓸 필요가 없습니다.
token 앞에
bearer
라는 문자열이 붙어야 하기 때문에 커스텀해줍니다.import { useRecoilValue } from 'recoil' import { userState } from '@/states/userState'; const Posting = () => { // location 코드 const { token } = useRecoilValue(useState); const customToken = `bearer ${token}`; }
렌더링
Styled
나 Container
가 붙은 경우는 모두 스타일 컴포넌트입니다. 이 중 StyledDesciprtion
은 단순히 포스팅을 위한 안내문구 (HEADER 상수) 입니다.하위 컴포넌트는 두가지입니다. 두 컴포넌트 모두 포스트를 작성하는 형식으로 명상 진행 횟수를 체크하기 때문에 받는 인자가 같습니다.
NewPost
글을 쓰기 위한 컴포넌트입니다.
PassPosting
건너뛰기 기능을 할 수 있는 컴포넌트입니다.
<LandingMain> <StyledPosting> <ContentContainer> <StyledDescription>{HEADER}</StyledDescription> <NewPost channelId={channelId} customToken={customToken} /> <PassPosting channelId={channelId} customToken={customToken} /> </ContentContainer> </StyledPosting> </LandingMain>
NewPost
NewPost
컴포넌트는 간단합니다. 글을 쓰는 영역과 글을 제출하는 버튼으로 이뤄져있습니다.<PostContainer> // 글쓰는 영역 <StyledTextArea ref={contentRef} required maxLength={500} placeholder={PLACEHOLDER} /> // 글 제출 버튼 <ButtonContainer> <Button width={300} height={50} dark={true} label={UPLOAD} bold={true} fontSize={16} borderRadius={10} handleClick={handleClickButton} /> </ButtonContainer> </PostContainer>
사용자 글 업로드
사용자가 쓴 글의 내용을 가져오기 위해 textArea 를 ref 로 참조했습니다.
const contentRef = useRef(null); return ( // ... <StyledTextArea ref={contentRef} required maxLength={500} placeholder={PLACEHOLDER} /> )
버튼을 클릭하면 서버에게 글을 저장해달라는 API 함수를 호출합니다.
그러기 위해 아래와 같은 이벤트 핸들러 함수를 버튼에 달아줍니다.
const handleClickButton = async() => { // 1. 사용자 글 유효성 검사 (XSS 공격, 빈 내용 등) if (validateContent(contentRef.current.value)) { // 2. formData 형식으로 보내기 위해 데이터 생성 const formData = createFormData(contentRef.current.value, channelId); // 3. API 호출 await postCreateNewPost(customToken, formData).then(() => { // 4. 완료 시 posts 페이지로 이동 navigate('/posts') }); } }
PassPosting
사용자가 건너뛰기 버튼을 누르면 빈 포스트를 서버로 전송합니다. 포스트의 개수로 명상횟수를 알 수 이고, 빈 포스트는 렌더링 되지 않습니다.
로직은
NewPost
와 비슷합니다. const PassPosting = () => { return ( <> <StyledPassPosting onClick={handleClickPassPost}> {PASS_POSTING} // '건너뛰기' 문자열 상수 변수 </StyledPassPosting> </> ); }
건너뛰기 클릭 시
빈 내용의
formData
를 생성하여 API 를 호출합니다.const handleClickPassPost = async () => { const formData = createFormData('', channelId); await postCreateNewPost(customToken, formData).then(() => { navigate('/posts'); }); };