Mannamchu. Refactoring
<메모>
https://www.notion.so/wecode/Westagram-React-Refactoring-Checklist-e102e9a7c60b4a1a8fddfd5a47b908f1
Import 순서 수정해주세요! 일반적인 convention을 따르는 이유도 있지만, 순서만 잘 지켜주셔도 가독성이 좋아집니다. 아래 순서 참고해주세요.
React → Library(Package) → Component → 변수 / 이미지 → css 파일(scss 파일)
라이브러리
React 관련 패키지
외부 라이브러리
컴포넌트
가까운 컴포넌트 to 먼 컴포넌트
함수, 변수 및 설정 파일
사진 등 미디어 파일(.png)
css 파일 (.scss)
전역에 적용되는 부분은 팀원들과 상의 후 common.scss으로 빼주시면 되겠습니다!
self closing 적용해주세요! 리뷰 남기지 않은 부분도 확인해주시기 바랍니다-!
tag selector로 태그에 직접적으로 스타일 속성을 부여하셨는데,
이후에 동일 태그를 사용하지만 다른 스타일을 부여할 경우 가 생기면 유지보수가 어렵습니다.
ex) input으로 인증번호를 받는, 다른 스타일의 입력창 추가
그렇기 때문에 가급적이면 className을 부여해서 사용해주시는 게 좋습니다!
제가 주석 처리하긴 했지만,, 불필요한 주석은 올릴 때 확인해서 삭제해주셔야 합니다!
다음과 같이 template literal을 이용해 작성할 수도 있습니다!
className={this.checkValid() ? "loginBtn" : "loginBtn disabled"}
className={`loginBtn ${this.checkValid() ? '' : 'disabled'}`}
console.log는 테스트할 때는 필수적이지만 최종 결과물에는 포함되면 안 됨.
id ・ class, 변수, 함수의 이름에서 의미가 직관적으로 드러나게 작성해주세요
코드는 쓰는 경우보다 읽히는 경우가 훨씬 많습니다!
handler 함수 한 번에 작성하기
수정 전
handleEmailInput = e => {
this.setState({
email: e.target.value,
});
};
handlePwInput = e => {
this.setState({
password: e.target.value,
});
};
handlePwCheckInput = e => {
this.setState({
passwordCheck: e.target.value,
});
};
handleNameInput = e => {
this.setState({
name: e.target.value,
});
};
handlePhoneInput = e => {
this.setState({
phone: e.target.value,
});
};
handleAddressInput = e => {
this.setState({
address: e.target.value,
});
};
수정 과정
각 input(email, pw, name...)의 event마다 handler 함수를 일일이 적어줬는데 (handleEmailInput, handlePwInput, handleNameInput...)
이때, "handler 함수 이름을 className과 동일하게 사용하겠다."라고 결정하면 먼저 두 가지가 변경된다.
1) 'handleEmailInput, handlePwInput, handleNameInput' 등등을 handleInput 하나로만 작성해도 됨. (내가 지금 input에 event를 주고 있어서 handleInput고 쓰는 거지, 꼭 handleInput이어야 하는 건 아님)
2) '각 className: e.target.value'라고 일일이 쓰던 걸 '[e.target.className] : e.target.value'라고만 작성해도 됨.
handleInput = e => {
this.setState({
[e.target.className] : e.target.value,
});
};
하지만 내 코드에선 위 코드가 정상적으로 실행되지 않았는데 (input에 작성한 함수가 작동하지 않았음), 그 이유는 state를 선언한 부분에서 back-end와 통신할 key값 외에 front-end단에서만 사용할 값도 state에 적어주느라 state 부분을 복수의 그룹으로 작성했기 때문이다.
아래 state 부분을 보면, back-end와 소통할 accountInfo 그룹 외에 passwordCheck와 addressDetail이라는 그룹이 있다.
class Signup extends Component {
constructor() {
super();
this.state = {
accountInfo: {
email: "",
password: "",
name: "",
phone: "",
address: "",
},
addressDetail: "",
passwordCheck: "",
};
}
그래서 아래처럼 handler 함수에 accountInfo가 어떤 부분인지 한 번 더 짚어주는 것으로 수정해 주어야 한다.
수정 후
handleInput = e => {
const necessaryInputs = {
...this.state.accountInfo,
[e.target.className]: e.target.value,
};
this.setState({ accountInfo: necessaryInputs });
};
전개구문 https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
https://www.notion.so/wecode/Westagram-React-Refactoring-Checklist-e102e9a7c60b4a1a8fddfd5a47b908f1
※ 요것은 js
https://www.notion.so/wecode/Westagram-JS-Refactoring-Checklist-dcff20de07b6449d89d82980e7af9cba
1. reset.scss & common.scss
reset.scss, common.scss의 위치는 index.js에서 한번만 작성한다.
reset.scss, common.scss
는 기본적으로index.js
에서 importindex.html - index.js - <Routes />
세 가지의 관계를 생각해보면 왜index.js
에서 한 번만 import 하면 되는지 쉽게 이해하실 수 있습니다 :)- 변수, mixin 등은
variables.scss, mixin.scss
별도 파일로 분리해서 필요한.scss
파일에서 import 해서 사용 - 초기화하는 세팅은
reset.scss
에 작성 - 모두가 같이 쓸 수 있는 css는
common.scss
에 작성
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
2. Sass nesting 활용
최종적으로 파일들이 하나로 합쳐지기 때문에 내가 작성한 코드가 의도치 않게 다른 파일에 있는 요소에 영향을 줄 수 있다.
- cf. SPA, React, React Router 개념
3. className의 동적 사용
style에 변화를 줄 때 inline으로 바로 줄 수도 있지만 css 파일에 일괄 작성
<button>
onClick={this.handleCommentInputBtn}
style={{ color: this.state.commentbtn ? "#0095f6" : "#b5e1ff" }}
<button
onClick={this.handleCommentInputBtn}
className={this.state.commentBtn ? "activated" : "deactivated"}
>
로그인
</button>
.activated {
color: #0095f6;
}
.deactivated {
color: #b5e1ff;
}
4. 비구조화 할당, 구조분해 할당 (destructuring)
비구조화 할당(구조분해 할당)은 객체, 배열에 적용할 수 있는 ES6 문법이다.
이 기능을 통해 긴 코드를 간결하게 쓸 수 있다.
handleInput = e => {
this.setState({
value: e.target.value,
})
...
}
// 비구조화 할당 적용
handleInput = e => {
const { value } = e.target;
this.setState({
value: value,
})
...
}
// key-value 이름이 같은 경우 (cf. ES6 객체 단축 속성명)
handleInput = e => {
const { value } = e.target;
this.setState({
value,
})
...
}
const {idValue} = this.state;
- 함수에서 뿐만 아니라
render
함수 안에서도 state, props 객체에 비구조화 할당을 적용해
코드를 간결하게 할 수 있다.
render() {
const { email } = this.state;
const { handleInput } = this;
return (
<input
onChange={handleInput}
value={email}
/>
)
}
5. boolean 데이터 타입의 활용
Boolean의 데이터 타입 특성을 이용하면 코드를 간결하고 쉽게 바꿀 수 있다.
if {this.state.idValue.includes("@" && event.target.value.length >= 5)}
this.setState({
loginBtnOpacity: true,
loginBtn: false
})
} else {
this.setState({
loginBtnOpacity: false,
loginBtn: true
})
}
const isValid = this.state.idValue.includes("@") && event.target.value.length >= 5;
this.setState({
loginBtnOpacity: isValid ? true : false,
loginBtn: isValid ? false : true
})
this.setState({
loginBtnOpacity: isValid,
loginBtn: !isValid,
})
6. 반복되는 코드는 Array.map() 활용
<li className="footer__li">
<a href="">도움말</a>
<li className="footer__li">
<a href="">홍보 센터</a>
<li className="footer__li">
<a href="">API</a>
<li className="footer__li">
<a href="">채용 정보</a>
<li className="footer__li">
<a href="">개인정보 처리 방침</a>
<li className="footer__li">
<a href="">약관</a>
<li className="footer__li">
<a href="">위치</a>
<li className="footer__li">
<a href="">인기 계정</a>
// footerData.js
// named export (vs. default export)
export const INFO = [
{id: 1, content: "도움말"},
{id: 2, content: "홍보 센터"},
{id: 3, content: "API"},
{id: 4, content: "채용정보"},
{id: 5, content: "개인정보처리방침"},
{id: 6, content: "약관"},
{id: 7, content: "위치"},
{id: 8, content: "인기 계정"},
]
// Footer.js (Footer 컴포넌트)
import { INFO } from './footerData.js'
render() {
return (
...
{INFO.map(el => {
return (
<li key={el.id}>
<a href="">{el.content}</a>
</li>
)
})}
- map을 사용할 때는 return 되는 JSX 요소마다 유니크한 key 값이 존재해야 한다.
- key 속성은 제일 바깥에 있는 태그에 부여한다.
Array.map(el => {
return (
<li key={el.id}>
<span>{el.content}</span>
</li>
)
})
- 위 상황에서 INFO 변수를
render
와return
사이에서 선언할 경우 컴포넌트가 render 될 때마다 새로운 변수가 계속 선언되기 때문에 컴포넌트 밖에서 선언한다.
(보통.js
혹은.json
파일로 분리해서 데이터를 관리한다.) - 참고 ) MDN | Array.prototype.map()
7. 계산된 속성명
을 이용한 inputHandler 함수 합치기
input
태그에는 name
이라는 속성이 있습니다.
말 그대로 input
태그에 이름을 지어 주는 건데요.
이 name
속성 덕분에 여러 개의 input handler를 하나로 합칠 수 있습니다.
// 비슷하게 생긴 코드가 보이면 줄이는 방법에 대해 고민해보시기 바랍니다!
// 기존
handleEmail = e => {
const { value } = e.target;
this.setState({
email: value,
})
...
}
handlePw = e => {
const { value } = e.target;
this.setState({
password: value,
})
...
}
render(){
return (
...
<input
className="emailInput"
type="text"
onChange={this.handleEmail}
/>
<input
className="pwInput"
type="password"
onChange={this.handlePw}
/>
...
)
}
// input에 name 추가, 함수 하나로
handleInput = e => {
const { value, name } = e.target;
this.setState({
[name]: value
})
}
render(){
return (
...
<input
className="emailInput"
type="text"
name="email"
onChange={this.handleInput}
/>
<input
className="pwInput"
type="password"
name="password"
onChange={this.handleInput}
/>
...
)
}
- name 속성의 값은 value처럼
e.target
으로 가져올 수 있습니다. (e.target.name
) - name 속성은 오직 input 태그에! 다른 태그에서 사용하려고 시도하지 마세요!
- 참고) 계산된 속성명 - computed property names
8. vs
<a>
tag는 사용하면 새로고침 하는 것처럼 html을 새로 다 받아 오는 반면,
<Link>
를 사용하면 컴포넌트만 바꿔줍니다.
렌더링 최적화를 위해서 <Link>
사용해주세요!
9. import
순서
- 라이브러리
- React 관련 패키지
- 외부 라이브러리
- 컴포넌트
- 가까운 컴포넌트 to 먼 컴포넌트
- 함수, 변수 및 설정 파일
- 사진 등 미디어 파일(
.png
) - css 파일 (.
scss
)
Author And Source
이 문제에 관하여(Mannamchu. Refactoring), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@maimade/CloneCoding5Refactoring저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)