본문 바로가기

React

React 프로젝트 Convention 간략 정리

#1. 개발환경

"@apollo/client": "^3.4.10",
"@mui/icons-material": "^5.0.0",
"@mui/material": "^5.0.0",
"classnames": "^2.3.1",
"clsx": "^1.1.1",
"dayjs": "^1.10.7",
"formik": "^2.2.9",
"graphql": "^15.5.3",
"lodash": "^4.17.21",
"react": "^16.13.1",
"react-router-dom": "^6.0.0-beta.0",
"vexflow": "^4.0.0",
"yup": "^0.32.9"

주요 개발 api들입니다. dependency 참고하시고, 신규 api가 추가된 경우에는 간단히 설명해서 알려주시면 도움이 될 것 같습니댜.

 

#2. 폴더 구조 및 파일 컨벤션

*폴더명은 소문자, 각 JSX파일은 대문자로 시작하는 .js 확장자 (.jsx, .ts는 사용하지 않음)

 

 -- build

 -- node_modules

 -- public

    --- images

        --- 폴더

    --- ogg

    --- soundfonts

-- src

    --- components

       ---- players  -> 테마기준, 페이지기준으로 분리하여, 폴더로 구분하여 component file을 생성합니다.

       ---- piano

       ...

    --- graphQL

       --> firestore collection 기준으로 파일을 만드시고, 필요할 경우, 신규 파일을 생성해도 좋습니다.

    --- pages

        --> 주요 메인 페이지(즉, 라우터를 통해 접속하게될 주요 페이지에 대한 파일을 작성하며)를 작성합니다. 같은 테마로 많은 파일이 생성될 경우에는 폴더를 생성해도 무방합니다.

   --- utils

        ---> components 중에서 helper함수나, 반복되는 custom mui components 등을 utils 폴더에 따로 저장합니다. 폴더를 생성해서 구분하여도 무방합니다.

 - App.js

 - index.js

 --> 그외 설정 파일들은 src 루트에 작성합니다. (.env, firebase.json, routes.js, ...etc)

 

* 폴더 구조는 이 형식을 기본으로 하며, 변경 설정이 필요한 경우 협의하여 재정의 합니다.

 

#3. react-router-dom v.6

router 관리는 신규버전인 version 6를 사용하기로 합니다.

useRoute를 사용하여 별도의 routes.js 파일에 저장하기로 합니다.

 

#4. react js file convention

- 상단에 module, graphQL, component,  mui, style 순서대로 import

- 파일 명 선언은 const 가 아닌 function 을 사용하여, style 선언이 파일 젤 아래에도 hoisting될 수 있도록 한다.

- useState, useRef, useEffect 등은 가급적 유형별로 정렬해준다. (절차적 문제가 있을 경우는 그러하지 않는다)

// import external module (dependency)
import React, { useState } from "react";
import { useParams, Link } from 'react-router-dom';
import _ from 'lodash';

// graphQL stuff
import { useQuery } from '@apollo/client';
import { GET_SONG } from '../graphQL/songs';

// components
import MidiKeyboard from '../components/players/MidiKeyboard';
import InputSheetProvider from '../components/players/InputSheetProvider';
import midifunctions from '../components/utils/midifunctions';
import Loading from '../utils/Loading';

// MUI stuff
import { Grid, makeStyles, Box } from "@material-ui/core";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import { BorderBottom } from "@mui/icons-material";

// styles
const useStyles = makeStyles((theme) => {
  return {
    root: {
      backgroundColor: theme.palette.background.paper,
      height: "100%",
      fontFamily: "NotoSansKR",
      backgroundImage: 'url("/images/background-star.svg")',
    },
    tableHeader: {
      width: "100%",
      backgroundColor: "#8295db",
      height: 47,
      display: "flex",
      alignItems: "center",
      color: "#0f2468",
      fontSize: 23,
      fontWeight: "bold",
      fontFamily: "Binggrae",
      paddingLeft: 15,
    },
    rankList: {
      display: "flex",
      width: theme.spacing(63),
      height: theme.spacing(6),
      justifyContent: "space-around",
      alignItems: "center",
      color: "#fff",
      fontFamily: "NotoSansKR",
      backgroundColor: "#02175b",
      borderBottom: "1px solid #6c789d",
      fontWeight: "bold",
    },
    container: {
      height: "calc(100vh - 98px)",
      width: "50vw",
      overflow: "hidden",
      display: "flex",
      flexDirection: "column",
      alignItems: "center",
    },
  };
});

// function [x const]
function Ranking(props) {
  const classes = useStyles();
 
  // useState
  // useEffect
  // useRef
  // useQuery
  
  // 중간 생략...
  return (
    <Page className={classes.root} title="우리원 랭킹">
      <Grid container>
        <Grid item xs={12}>
          <PianoHeader title="우리원 랭킹" />
        </Grid>
        <Grid item xs={12} className={classes.container}>
        <Box
            sx={{
              margin: "16px 32px",
              width: "100%",
              borderRadius: 20,
              width: 1000,
              overflow: "auto",
            }}
          >
            <Box className={classes.tableHeader}>연주 랭킹</Box>
            <Box
              style={{
                overflow: "hidden",
                height: "30vmin",
                display: "flex",
                flexDirection: "column",
                flexWrap: "wrap",
              }}
            >
              {rankingDate.map((rankingDate, index) => (
                <Box style={{ padding: 0 }} key={index}>
                  <Box className={classes.rankList}>
                    <Box sx={{ fontSize: "30px", color: "#fff", width: 30 }}>
                      {rankingDate.rank}
                    </Box>
                    <Box
                      sx={{
                        display: "flex",
                        justifyContent: "center",
                        alignItems: "center",
                      }}
                    >
                      <Box mr={2}>
                        <img src={`/images/icon/${rankingDate.icon}.svg`} />
                      </Box>
                      {rankingDate.name}
                    </Box>
                    <Box sx={{ fontSize: "13px" }}>{rankingDate.song}</Box>
                    <Box
                      sx={{
                        fontSize: "30px",
                        color: "#ffd500",
                        fontFamily: "Binggrae",
                      }}
                    >
                      {rankingDate.point}
                    </Box>
                  </Box>
                </Box>
              ))}
            </Box>
          </Box>
          <Box
            sx={{
              margin: "16px 32px",
              width: "100%",
              borderRadius: 20,
              width: 1000,
              overflow: "auto",
            }}
          >
            <Box className={classes.tableHeader}>게임 랭킹</Box>
            <Box
              style={{
                overflow: "hidden",
                height: "30vmin",
                display: "flex",
                flexDirection: "column",
                flexWrap: "wrap",
              }}
            >
              {rankingDate.map((rankingDate, index) => (
                <Box style={{ padding: 0 }} key={index}>
                  <Box className={classes.rankList}>
                    <Box sx={{ fontSize: "30px", color: "#fff", width: 30 }}>
                      {rankingDate.rank}
                    </Box>
                    <Box
                      sx={{
                        display: "flex",
                        justifyContent: "center",
                        alignItems: "center",
                      }}
                    >
                      <Box mr={2}>
                        <img src={`/images/icon/${rankingDate.icon}.svg`} />
                      </Box>
                      {rankingDate.name}
                    </Box>
                    <Box sx={{ fontSize: "13px" }}>{rankingDate.song}</Box>
                    <Box
                      sx={{
                        fontSize: "30px",
                        color: "#ffd500",
                        fontFamily: "Binggrae",
                      }}
                    >
                      {rankingDate.point}
                    </Box>
                  </Box>
                </Box>
              ))}
            </Box>
          </Box>
          
        </Grid>
      </Grid>
    </Page>
  );
}

export default Ranking;

#5. 이미지파일 등 퍼블릭 파일처리

 - 이미지 파일 등 외부에 공개될 파일들은 가급적 public 폴더에 묶어서 정리하고,

 - 상대경로를 사용한다.

 - public의 경로가 '/' 인지, './' 인지는 미리 확인한다 (아래의 경우  / 임) 

<Box className={classes.lessonBtn} onClick={() => alert('컨텐츠 준비중입니다!')}>
    <img src="/images/lesson/lesson-btn.png" alt="레슨 시작" className={classes.btnImage} />
    <Typography className={classes.btnTitle}>레슨 고고</Typography>
</Box>

- public 경로를 확인할 때는, firebase hosting을 사용하기 때문에, firebase로 deploy할 때, 아래 그림과 같이 terminal에서 확인할 수 있다. (hosted at /.)

#6. File Name Convention

위에서 선언되지 않은 것들은, 일반적인 스타일 가이드를 따른다. (airbnb js 스타일가이드 참조)

https://airbnb.io/javascript/react/ 

 

Airbnb React/JSX Style Guide

A mostly reasonable approach to JavaScript

airbnb.io

 

세부적인 내용은 계속 업데이트 하겠습니다. 

 

참고하시기 바랍니다.

 

'React' 카테고리의 다른 글

React 학습 영상  (0) 2023.02.01
메모  (0) 2022.04.13