[React] 익명게시판 만들어보기 2

김유진·2022년 6월 29일
0

React

목록 보기
5/64

React를 이용하여 간단한 익명게시판을 만들었다. 이제 몇가지 기능을 추가하여서 더욱 익명게시판다운 모습을 만들어 보이도록 하자. 익명게시판에서 지원하는 기능은 다음과 같다.

  1. 새로고침을 하면 새로운 게시글이 업로드된다.
  2. 수동으로 조작하던 DarkMode를 진짜 버튼 구실을 할 수 있도록 만들어보자.(아이콘 클릭시 각 모드 전환)

1. Component 분리하기

우리가 지난 시간에는 모든 Component 요소를 무식하게 App.jsx 에 모두모두 몰아 넣었다. 이제 Header, PostList, Footer, Slogun부분으로 나누어서 Component를 분리해보자. 사실은 노가다 작업인데, StyleComponent 몇가지 이름이 겹치는 게 있어서 그것 좀 바꿔주느라고 노가다를 했다.

Header.jsx

import {
    HeaderDiv,
    TitleLogoDiv,
    TitleBig,
    TitleSmall,
    SubHeaderDiv,
} from './styledComponent';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import { faSun, faMoon } from '@fortawesome/free-solid-svg-icons';

function Header({darkMode, setDarkMode}) {
    const toggleDarkMode = () => {
        setDarkMode((darkMode)=> ! darkMode);
    };
    return (
        <HeaderDiv>
            <TitleLogoDiv>
                <TitleBig>고민</TitleBig>
                <TitleSmall>익명게시판</TitleSmall>
            </TitleLogoDiv>
            <SubHeaderDiv>
                {darkMode ? (
                    <div>
                        <FontAwesomeIcon onClick = {toggleDarkMode} icon = {faSun}/>
                    </div>
                ) : (
                    <div>
                        <FontAwesomeIcon onClick = {toggleDarkMode} icon = {faMoon}/>
                    </div>
                )}
            </SubHeaderDiv>
        </HeaderDiv>
    );
}
export default Header

ShowPostList.jsx

import {
    EachPostLi,
    LoadingDiv,
    LoadingImg,
    PagenumberDiv,
    PagingSection,
    PostLink,
    PostListDiv,
    PostRepl,
    PostSection,
    PostTitle,
    PostTitleDiv,
} from './styledComponent';

import {
    faSun,
    faMoon,
    faArrowsRotate,
    faPenToSquare,
    faLocationPin,
    faArrowLeft,
    faArrowRight,
    faSlidersH,
} from '@fortawesome/free-solid-svg-icons';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';


function ShowPostList({isPost, loading}){
    return (
        <>
            <PostSection>
                    <PostTitleDiv>
                        <FontAwesomeIcon icon = {faArrowsRotate} />
                        <PostTitle>익명게시판</PostTitle>
                        <FontAwesomeIcon icon = {faPenToSquare} />
                    </PostTitleDiv>
                    <PostListDiv>
                        {loading ? (
                                <LoadingDiv>
                                    <LoadingImg src = {'./loading.svg'}/>
                                </LoadingDiv>
                            ) : isPost ? (
                                    <LoadingDiv>
                                        아직 기록된 글이 없습니다.
                                    </LoadingDiv>
                            ) : (
                                <ul>
                                    <EachPostLi>
                                        <div>
                                            <FontAwesomeIcon 
                                                icon ={faLocationPin}
                                            />
                                            <PostLink>
                                                React가 너무 재미있어요.
                                            </PostLink>
                                        </div>
                                        <PostRepl>[35]</PostRepl>
                                    </EachPostLi>
                                </ul>
                            )}
                    </PostListDiv>
                </PostSection>
                <PagingSection>
                    <PagenumberDiv>
                        <FontAwesomeIcon icon = {faArrowLeft}/>
                    </PagenumberDiv>
                    <PagenumberDiv>2</PagenumberDiv>
                    <PagenumberDiv>
                        <FontAwesomeIcon icon ={faArrowRight}/>
                    </PagenumberDiv>
                </PagingSection>
        </>
    )
}

export default ShowPostList

Slogun.jsx

import {
    SlogunBig,
    SlogunSection,
    SlogunSmall,
} from './styledComponent';

function Slogun(){
    return (
        <SlogunSection>
            <SlogunBig>자유롭게 의견을 나누어요</SlogunBig>
            <SlogunSmall>악플은 금물!</SlogunSmall>
        </SlogunSection>
    )
}

export default Slogun

Footer.jsx

import {
    FooterDiv,
    FooterSmall,
    FooterBig,
} from './styledComponent';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faReact } from '@fortawesome/free-brands-svg-icons';

function Footer(){
    return (
        <FooterDiv>
            <FontAwesomeIcon icon = {faReact} />
            <FooterBig>for react study</FooterBig>
            <FooterSmall>2022.by Eugene</FooterSmall>
        </FooterDiv>
    )
}

export default Footer

App.jsx

import {
    Main,
    MediaDiv,
} from './styledComponent';

import Header from './Header';
import Slogun from './Slogun';


import { darkTheme, GlobalStyles, lightTheme } from './styles';
import { ThemeProvider } from 'styled-components';
import {useState} from 'react';
import ShowPostList from './ShowPostList';
import Footer from './Footer';

function App() {
    const [darkMode, setDarkMode] = useState(true);
    const loading = false;
    const isPost = false;
    return (
        <>
            <ThemeProvider theme ={darkMode ? darkTheme : lightTheme}>
                <GlobalStyles/>
                <MediaDiv>
                    <Header darkMode = {darkMode} setDarkMode = {setDarkMode}/>
                    <Main>
                        <Slogun />
                        <ShowPostList/>
                    </Main>
                    <Footer />
                </MediaDiv>
            </ThemeProvider>    
        </>
    )
}

export default App;

모든 컴포넌트 분리 완료!! 이제 props & state를 이용하여 구현하고자 하는 기능을 구현해보자.

2. 새로고침 버튼을 통하여 게시글 리스트 나타내기

먼저 배열 형식으로 게시글의 제목과 댓글을 정리해야한다. 이를 먼저 구현해봅시다. EachPostLi라는 새로운 컴포넌트를 만들어둡시다. 그리고 아래 코드와 같이 작성해볼까요~이 컴포넌트는 각각의 게시글을 나타내는데 쓰이는 요소입니다^^
EachPostLi.jsx

import {
    EachPostLi,
    PostLink,
    PostRepl,
} from './styledComponent';

import {
    faLocationPin,
} from '@fortawesome/free-solid-svg-icons';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

function EachPost({title,replCount}) {
    return (
        <EachPostLi>
            <div>
                <FontAwesomeIcon icon ={faLocationPin} />
                <PostLink>
                    {title}
                </PostLink>
            </div>
            <PostRepl>{replCount}</PostRepl>
        </EachPostLi>
    );
}

export default EachPost;

기존에 있던 내용을 지우고, props 값을 받아와 사용하기 위하여 인자로 {title,replCount}를 넘겨받습니다. 그리고 그 값을 사용하기 위하여 {title}{replCount} 값을 넣어요.
그럼 내가 생성하는 값에 따라서 자동으로 여기다가 값을 넣어주는 것입니다.

이제 App,jsx 파일을 수정해보자.

import {
    Main,
    MediaDiv,
} from './styledComponent';

import Header from './Header';
import Slogun from './Slogun';
import ShowPostList from './ShowPostList';
import Footer from './Footer';

import { darkTheme, GlobalStyles, lightTheme } from './styles';
import { ThemeProvider } from 'styled-components';
import {useState} from 'react';;

function App() {
    const initialPostList = [
        {id : 1, title : '리액트 공부는 재미있어.', replCount : 1},
        {id : 2, title : '프론트 공부 열심히 해.', replCount : 43},
        {id : 3, title : '파이팅^^.', replCount : 1},
    ];
    const [darkMode, setDarkMode] = useState(true);
    const [loading, setLoading] = useState(false);
    const [isPost, setIsPost] = useState(false)
    const [postList, setPostList] = useState(initialPostList);

    const addPost = () => {
        setPostList((postList) => [
            ...postList, {id : 4, title :'공부는 열심히 하면 좋아.', replCount : 21},
        ]);
    };

    return (
        <>
            <ThemeProvider theme ={darkMode ? darkTheme : lightTheme}>
                <GlobalStyles/>
                <MediaDiv>
                    <Header darkMode = {darkMode} setDarkMode = {setDarkMode}/>
                    <Main>
                        <Slogun />
                        <ShowPostList loading = {loading} isPost = {isPost} postList = {postList} addPost = {addPost}/>
                    </Main>
                    <Footer />
                </MediaDiv>
            </ThemeProvider>    
        </>
    )
}

export default App;

값을 변경할 수 있도록 useState를 사용하여 직접 인수값을 넘겨받는 형태로 바꾸었다.

  • isPost : 작성된 포스트가 존재하는지 확인하는 변수
  • postList : 포스트가 작성된 내용을 배열로 저장한 변수

이 변수들을 생성해 주었으니 각각의 Component에 값을 내려주어야 한다. 그래서 다음과 같은 코드들을 작성한 것!

<Header darkMode = {darkMode} setDarkMode = {setDarkMode}/>
<ShowPostList loading = {loading} isPost = {isPost} postList = {postList} addPost = {addPost}/>

여기서 addPost 함수의 기능은, 새로고침 버튼을 눌렀을 때, 게시글 목록에 새로운 게시글을 추가해주는 것이다. postList에 새로운 리스트를 추가하는 것이 함수의 기능이다.
그리고 initialPostList는 작성된 게시글을 리스트로 보여주는 형식을 가지고 있다. 각각의 id, post, 댓글수를 요소로 가지고 있고 배열의 형태로 저장되어 화면에 출력되는 것이다.

다만 여기서 주의해야 할 점은 useState를 사용하기 위하여 이것을 꼭 import 해주어야 한다.

각각의 Post를 보여주는 EachPost.jsx를 생성하였으니 기존에 포스트를 보여주던 jsx 파일을 수정하여 보자. 바로 ShowPostList.jsx이다!

...
import EachPost from './EachPost';


function ShowPostList({ isPost, loading, addPost, postList }){
    return (
        <>
            <PostSection>
                    <PostTitleDiv>
                        <FontAwesomeIcon onClick = {addPost} icon = {faArrowsRotate} />
                        <PostTitle>익명게시판</PostTitle>
                        <FontAwesomeIcon icon = {faPenToSquare} />
                    </PostTitleDiv>
                    <PostListDiv>
                        {loading ? (
                                <LoadingDiv>
                                    <LoadingImg src = {'./loading.svg'}/>
                                </LoadingDiv>
                            ) : isPost ? (
                                    <LoadingDiv>
                                        아직 기록된 글이 없습니다.
                                    </LoadingDiv>
                            ) : (
                                <ul>
                                    {postList.map((element) => (
                                        <EachPost 
                                        key={element.id} 
                                        title = {element.title}
                                        replCount = {element.replCount}
                                        />
                                    ))}
                                </ul>
                            )}
                    </PostListDiv>
...

역시나 ShowPostList는 네 개의 변수인 isPost, loading, addPost,postList를 받습니다. 그리고 이를 이용하여 자신들의 할 일을 수행하지요. onClick을 통하여, addPost라는 이벤트를 발생시키고, 상단의 아이콘을 클릭하면 새로운 글이 자꾸 추가되는 것을 관찰할 수 있다. 그리고 map을 이용하여, 배열을 순서대로 잘 나타내고 있다. 이럴 때 map이라는 함수를 사용한다는 점을 잊지 말자!

이렇게 하면 새로고침을 할 때마다 새로운 게시글이 올라오게 된다.

3. 다크모드 아이콘 활성화시키기

다크모드 아이콘 활성화도 똑같이 useState를 이용하고, onclick 을 사용하면 된다.
App,jsx 코드를 수정하여 보자.

function App() {
 ...
    const [darkMode, setDarkMode] = useState(true);
...

    return (
        <>
            <ThemeProvider theme ={darkMode ? darkTheme : lightTheme}>
                <GlobalStyles/>
                <MediaDiv>
                    <Header darkMode = {darkMode} setDarkMode = {setDarkMode}/>
                    <Main>
                        <Slogun />
                        <ShowPostList loading = {loading} isPost = {isPost} postList = {postList} addPost = {addPost}/>
                    </Main>
                    <Footer />
                </MediaDiv>
            </ThemeProvider>    
        </>
    )
}

export default App;

그리고 Header.jsx로 이동하여 다음 코드를 수정해보자.

...
function Header({darkMode, setDarkMode}) {
    const toggleDarkMode = () => {
        setDarkMode((darkMode)=> ! darkMode);
    };
    return (
        <HeaderDiv>
            <TitleLogoDiv>
                <TitleBig>고민</TitleBig>
                <TitleSmall>익명게시판</TitleSmall>
            </TitleLogoDiv>
            <SubHeaderDiv>
                {darkMode ? (
                    <div>
                        <FontAwesomeIcon onClick = {toggleDarkMode} icon = {faSun}/>
                    </div>
                ) : (
                    <div>
                        <FontAwesomeIcon onClick = {toggleDarkMode} icon = {faMoon}/>
                    </div>
                )}
            </SubHeaderDiv>
        </HeaderDiv>
    );
}
export default Header

onClick을 통하여, toggleDarkMode라는 함수를 실행시켜주었다.

이렇게 해서 완성된 두번째 익명게시판이다.

0개의 댓글