#1. 개발환경
주요 개발 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 |