HTML, CSS, JS 기반 refactoring 에 대해 다룬 지난 글에 이어서 이번에는 React로 만든 코드의 refactoring 가이드라인을 해보았다. 백번 강조해도 부족하지 않을 가독성 향상과 유지보수의 용이성을 위해.. 오늘도 리팩토링을 해보자.
점점 벨로그 글 쓰는 시간보다 짤 찾는 시간이 더 길어지는 기분이다
제일 첫번째로 React 문서의 최상단에 정리되지 않은 import부터 정리해주자.
// 정리 전
import React, { useEffect, useState } from 'react';
import './Main.scss';
import logo from"./assets/logo.png";
import {useNavigate} from 'react-router-dom;
import Comment from './Comment';
import WestaAside from './WestaAside';
import Feed from './Feed';
권장 import 순서:
// 정리 후
import React, { useEffect, useState } from 'react';
import {useNavigate} from 'react-router-dom;
import Feed from './Feed';
import Comment from './Comment';
import WestaAside from './WestaAside';
import logo from"./assets/logo.png";
import './Main.scss';
각 기능별로 순서별로 정리하니 가독성이 올라갔다 :)
한 가지 함수에 많은 기능이 함유되어 있다면 원하는 일부분의 기능만 사용하기가 쉽지 않다. 리액트의 최고 장점인 재사용성을 극대화하려면 함수는 한 가지 기능만 수행하도록 최대한 작게 쪼개야 한다.
// 분리 전
const handleSignupAndLogin = () => {...};
// 분리 후
const handleSignup = () => {...};
const handleLogin = () => {...};
ES6의 문법 중 하나로 비구조화 할당을 통해 배열이나 객체의 속성을 해체하여 변수에 담을 수 있다. 비구조화 할당의 특징으로는 코드의 직관성과 간결성을 높일 수 있고 두번 이상 객체의 이름을 넣는 경우에 사용하는 것을 권장한다.
// 일반 함수
const student = {
name : "mikio",
className : "Algorithm",
result: "A"
};
const getStudentInfo = ({name, className, result}) => {
return '학생의 이름은 ${name}이고 수강한 강의명은 ${className}이며 성적은 ${result}입니다..';
getStudentInfo(student);
// 컴포넌트 함수
// StudentList.js
const StudentList = () => {
return (
<ul>
{STUDENT_LIST.map(student => (
<li key = {student.id}>
<StudentResult student = {student} />
</li>
))}
</ul>
);
};
// StudentResult.js
const StudentResult = ({student: { name, grade, result }}) => {
return (
<>
<span>{name}</span>
<span>{grade}</span>
<span>{result}</span>
</>
);
};
이 역시 ES6부터 지원되는 기능으로 계산된 속성명이란 객체의 속성명이 동적으로 결정되는 것을 의미한다.
input
태그의 name
이라는 속성과 계산된 속성명을 함께 이용하면 여러 개의 input handler를 합칠 수 있다. (name 속성은 오직 input 태그에서만 사용할 수 있다.)
수정 전)
수정 후)
if 조건문으로 작성한 코드를 Boolean 데이터 타입 특성인 삼항 연산자나 논리연산자를 활용하면 더 직관적이고 간결한 코드작성이 가능해진다.
// Before
const ChangeBtn = () => {
if (input.id.includes('@') && input.pw.length >= 5) {
setIsValid(true);
} else {
setIsValid(false);
}
};
// After1 : 삼항 연산자 활용
const ChangeBtn = () => {
input.id.includes('@') && input.pw.length >= 5
? setIsValid(true)
: setIsValid(false);
};
// After2 : 논리 연산자 활용
const ChangeBtn = () => {
const isValid = input.id.includes('@') && input.pw.length >= 5;
setIsValid(isValid);
};
인라인 스타일은 가장 높은 우선 순위를 가지기 때문에 CSS에서 지정한 스타일이 적용되지 않을수도 있으며 유지 보수가 어렵고 재사용성에 좋지 않다. 또한 CSS 클래스가 인라인 스타일보다 더 나은 성능을 보이므로 CSS 클래스 사용을 권장한다.
// Bad
<button style = {{color : isValid ? 'blue' : 'white'}}>
로그인
</button>
import 'style.css';
<button className ={isValid ? 'activated' : 'deactivated'}>
로그인
</button>
<Link>
는 브라우저의 주소만 바꿀 뿐 페이지를 새로 불러오지 않아서 렌더링 최적화를 위해서는 꼭 사용하는 것이 좋다. 반면에 html의 <a>
태그를 사용하면 페이지 자체를 새롭게 불러오게 되면서 앱의 상태가 초기화되고 렌더링 되니 컴포넌트도 모두 사라지고 새로 렌더링을 해야 한다. 따라서 React를 사용한 개발 과정에서는 <Link>
를 사용하는 것을 권장한다.
map 함수를 사용하면 불필요한 하드코딩 과정을 줄일 수 있다. 또한 데이터가 새로 추가되는 과정에서 유지보수하기 더욱 용이해진다.
//Bad
const Footer = () => {
return (
<div>
<li>
<Link to="/help">도움말</Link>
</li>
<li>
<Link to="/promotion">홍보 센터</Link>
</li>
<li>
<Link to="/api">API</Link>
</li>
<li>
<Link to="/recruitment">채용정보</Link>
</li>
<li>
<Link to="/privacy">개인정보처리방침</Link>
</li>
</div>
);
};
// Good
// footerData.js
export const INFO_LIST = [
{ id: 1, content: '도움말', url: '/help' },
{ id: 2, content: '홍보 센터', url: '/promotion' },
{ id: 3, content: 'API', url: '/api' },
{ id: 4, content: '채용정보', url: '/recruitment' },
{ id: 5, content: '개인정보처리방침', url: '/privacy' },
];
// Footer.js
import React from 'react';
import { Link } from 'react-router-dom';
import { INFO_LIST } from './footerData';
const Footer = () => {
return (
<div>
{INFO_LIST.map(info => {
return (
<li key={info.id}>
<Link to={info.url}>{info.content}</Link>
</li>
);
})}
</div>
);
};
INFO_LIST 같이 값이 변하지 않는 변수(상수)를 컴포넌트의 바디 안에서 선언할 경우 컴포넌트가 리렌더링 될 때마다 새로운 변수로 계속 선언된다.. 그러므로 컴포넌트 바디 밖에서 선언하거나 따로 파일을 생성하여 분류하는 것이 좋다.