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

김유진·2022년 6월 28일
0

React

목록 보기
3/64
post-custom-banner

React를 이용하여 간단한 익명게시판을 만들어보자.
익명게시판에서 지원하는 기능은 다음과 같다.

Dark Mode
Dark Mode와 Light Mode 조건부 렌더링 UI 구현

Loading

  • Loading True : 로딩중일 경우, 로딩 이미지를 띄운다.
  • Loaindg False : 게시글을 표시한다.
  • Loading False && No Post : 로딩이 끝났지만, 표시할 포스트가 없음을 안내한다.

1. 프레임 만들기

index.css

@import url('https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=League+Gothic&family=Noto+Sans+KR&display=swap');

body {
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
        'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
        'Helvetica Neue', sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

code {
    font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
        monospace;
}

textarea {
    resize: none;
}

html {
    width: 100%;
}

웹폰트를 불러와 미리 지정하고 (Black Han Sans, Noto Sans KR, Leauge Gothic)
html 가로폭을 100%로 채워 준 다음 resize:none을 지정하여 폰트 크기 재지정을 방지하였다.
다음 파일을 작성하여 보자.

styledComponent.js

import styled from 'styled-components';

export const MediaDiv = styled.div`
    margin: 0px auto;
    min-height: 100vh;
    width: 768px;
    color: ${(props) => props.theme.fontColor};
    background-color: ${(props) => props.theme.bgColor};
    @media screen and (max-width: 768px) {
        width: 100%;
    }
`;
export const Header = styled.div`
    width: 768px;
    height: auto;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    font-family: 'Black Han Sans', sans-serif;
    position: absolute;
    @media screen and (max-width: 768px) {
        width: 100%;
    }
`;

export const TitleLogoDiv = styled.div`
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    margin: 15px;
    line-height: 1;
`;

export const TitleBig = styled.span`
    font-size: 40px;
`;

export const TitleSmall = styled.span`
    font-size: 20px;
`;

export const SubHeaderDiv = styled.div`
    margin: 15px;
    font-size: 25px;
    display: flex;
    flex-direction: row;
`;

export const Main = styled.div`
    padding-top: 90px;
`;

export const SlogunSection = styled.div`
    margin-top: 10px;
    width: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
`;

export const SlogunBig = styled.span`
    font-size: 40px;
    font-family: 'League Gothic', sans-serif;
`;

export const SlogunSmall = styled.span`
    margin: 3px;
    font-weight: bold;
    color: #f39926;
`;
export const PostSection = styled.div`
    margin: 0px auto;
    margin-top: 20px;
    width: 90%;
    display: flex;
    flex-direction: column;
`;
export const PostTitleDiv = styled.div`
    border-top-left-radius: 30px;
    border-top-right-radius: 30px;
    color: white;
    background-color: #f39926;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: center;
`;
export const PostTitle = styled.span`
    margin-top: 10px;
    margin-bottom: 5px;
    font-family: 'Black Han Sans', sans-serif;
    font-size: 25px;
`;

export const PostListDiv = styled.div`
    font-size: 16px;
    font-family: 'Noto Sans KR', sans-serif;
`;

export const LoadingDiv = styled.div`
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    margin-top: 15px;
`;

export const LoadingImg = styled.img`
    width: 30px;
`;

export const EachPostLi = styled.li`
    margin: 14px;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
`;

export const PostLink = styled.span`
    margin-left: 5px;
`;
export const PostRepl = styled.div`
    font-family: 'Courier New', Courier, monospace;
`;
export const PagingSection = styled.section`
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    margin: 0px auto;
    width: 150px;
    margin-top: 20px;
`;
export const PagenumberDiv = styled.div`
    width: 30px;
    height: 30px;
    display: flex;
    justify-content: center;
    align-items: center;
    border: 1px solid #f39926;
    border-radius: 5px;
`;
export const Footer = styled.div`
    margin-top: 30px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
`;
export const FooterBig = styled.span`
    margin: 5px;
    font-size: 12px;
`;
export const FooterSmall = styled.span`
    margin: 5px;
    font-size: 5px;
`;

스타일을 지정할 컴포넌트도 미리 지정을 해 놓습니다. 컴포넌트를 미리 작성, 및 스타일링 해 놓은 것입니다. styled-components 패키지를 다운로드 하였으니 이를 활용하여 컴포넌트를 만듭니다. 전체 길이를 다 쓰면 너무 길이가 기니까, styled라고 줄여서 import합니다. 이제 각 스타일을 지정하여 스타일에 관련한 태그를 생성합니다.

다음으로 styles.js 파일을 작성합니다.

import { createGlobalStyle } from 'styled-components';

import reset from 'styled-reset';

export const lightTheme = {
    fontColor : '#2c2c2c',
    bgColor : 'white',
};

export const darkTheme = {
    fontColor:'white',
    bgColor:'#2c2c2c',
};

export const GlobalStyles = createGlobalStyle`
    ${reset}
`;

여기서 createGlobalStyle은 일괄적으로 전역으로 스타일을 적용하는 것입니다.
${reset}은 브라우저의 기본 디자인, 기본 여백 등을 없애주는 초기화 역할을 합니다.
styled-reset은 라이브러리를 설치해야 하기 때문에 명령어를 입력하여 설치를 해봅시다~~ >>명령어는 yarn add styled-reset 다음으로 lightThemedartTheme 부분은 라이트, 다크테마를 작성하기 위하여 존재하는 부분입니다! 각각 테마에 알맞게 해당하는 css를 작성해 넣었습니다.

다음으로는 App.jsx 파일입니다.
우리가 작성한 컴포넌트에 대해서 모두 import 되어 있는 것을 확인할 수 있습니다.

  1. styledComponent에 작성하였던 내용을 가지고 옵니다.
  2. 폰트와 아이콘을 import 합니다.
  3. 그리고 Fortawesome이라는 아이콘을 사용하였으니 이 친구들을 또 설치해주어야 합니다! 다음 명령어에 따라서 아이콘 확장팩을 설치해봅시다. 명령어는 다음과 같습니다.
    yarn add @fortawesome/free-solid-svg-icons @fortawesome/react-fontawesome @fortawesome/fontawesome-svg-core @fortawesome/free-brands-svg-icons
  4. 우리가 작성한 다크모드, 라이트모드에 대한 CSS코드를 import합니다.
import {
    EachPostLi,
    Footer,
    FooterBig,
    FooterSmall,
    Header,
    LoadingDiv,
    LoadingImg,
    Main,
    MediaDiv,
    PagenumberDiv,
    PagingSection,
    PostLink,
    PostListDiv,
    PostRepl,
    PostSection,
    PostTitle,
    PostTitleDiv,
    SlogunBig,
    SlogunSection,
    SlogunSmall,
    SubHeaderDiv,
    TitleBig,
    TitleLogoDiv,
    TitleSmall,
} from './styledComponent';
import {
    faSun,
    faMoon,
    faArrowsRotate,
    faPenToSquare,
    faLocationPin,
    faArrowLeft,
    faArrowRight,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faReact } from '@fortawesome/free-brands-svg-icons';
import { darkTheme, GlobalStyles, lightTheme } from './styles';
import { ThemeProvider } from 'styled-components';
import loadingIcon from './loading.svg';

function App() {
    const darkMode = true;
    const loading = false;
    const isPost = true;
    return (
        <>
            <ThemeProvider theme ={darkMode ? darkTheme : lightTheme}>
                <GlobalStyles/>
                <MediaDiv>
                    <Header>
                        <TitleLogoDiv>
                            <TitleBig>고민</TitleBig>
                            <TitleSmall>익명게시판</TitleSmall>
                        </TitleLogoDiv>
                        <SubHeaderDiv>
                            {darkMode ? (
                                <div>
                                    <FontAwesomeIcon icon = {faSun}/>
                                </div>
                            ) : (
                                <div>
                                    <FontAwesomeIcon icon = {faMoon}/>
                                </div>
                            )}
                        </SubHeaderDiv>
                    </Header>
                    <Main>
                        <SlogunSection>
                            <SlogunBig>자유롭게 의견을 나누어요</SlogunBig>
                            <SlogunSmall>악플은 금물!</SlogunSmall>
                        </SlogunSection>
                        <PostSection>
                            <PostTitleDiv>
                                <FontAwesomeIcon icon = {faArrowsRotate} />
                                <PostTitle>익명게시판</PostTitle>
                                <FontAwesomeIcon icon = {faPenToSquare} />
                            </PostTitleDiv>
                            <PostListDiv>
                                {loading ? (
                                        <LoadingDiv>
                                            <LoadingImg src = {loadingIcon}/>
                                        </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>
                    </Main>
                    <Footer>
                        <FontAwesomeIcon icon = {faReact} />
                        <footerBig>for react study</footerBig>
                        <FooterSmall>2022.by Eugene</FooterSmall>
                    </Footer>
                    
                </MediaDiv>
            </ThemeProvider>    
        </>
    )
}

export default App;

다크모드와 라이트모드에 따라서 적용되는 테마를 다르게 하기 위하여 <ThemeProvider theme ={darkMode ? darkTheme : lightTheme}> 와 같이 ThemeProvider로 감싸고, 삼항연산자를 이용하였습니다.
그리고 아래의 태그들이 전역테마에 의해서 결정되므로, <GlobalStyles/> 태그를 작성하여줍니다.

그리고 여기 코드를 유심히 봅시다.

<PostListDiv>
                                {loading ? (
                                        <LoadingDiv>
                                            <LoadingImg src = {loadingIcon}/>
                                        </LoadingDiv>
                                    ) : isPost ? (
                                            <LoadingDiv>
                                                아직 기록된 글이 없습니다.
                                            </LoadingDiv>
                                    ) : (
                                        <ul>
                                            <EachPostLi>
                                                <div>
                                                    <FontAwesomeIcon 
                                                        icon ={faLocationPin}
                                                    />
                                                    <PostLink>
                                                        React가 너무 재미있어요.
                                                    </PostLink>
                                                </div>
                                                <PostRepl>[35]</PostRepl>
                                            </EachPostLi>
                                        </ul>
                                    )}
                            </PostListDiv>

이곳의 코드는 로딩을 하고 있을 때, 로딩 아이콘이 존재하도록 하고, 로딩이 완료되었을 때에는 만약 포스트가 없다면 아직 기록된 글이 없다면 아직 기록된 글이 없습니다.로 내를 해줍니다. 그러나, 기뢱된 글이 있다면, 글을 나타내는 아이콘, 댓글이 함께 보일 수 있도록 <PostListDiv> 부분을 구성하였다.

post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 10월 20일

익명게시판 코드 깃허브에 공유 해주실 수 있나요? 따라해보려는데 안됩니다...

답글 달기