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

scss
https://www.notion.so/wecode/React-Sass-ce29fb27f0a244e2bcfead3beaf961c0#087d0fb8f7ae4533981b23eef461289c

https://www.notion.so/wecode/Westagram-React-Refactoring-Checklist-e102e9a7c60b4a1a8fddfd5a47b908f1

※ 요것은 js
https://www.notion.so/wecode/Westagram-JS-Refactoring-Checklist-dcff20de07b6449d89d82980e7af9cba

https://www.google.com/search?q=%EC%9D%B4%EB%A9%94%EC%9D%BC+%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D&newwindow=1&sxsrf=AOaemvJN1e32GMSjwzuD9FDU7o5JqJARfA%3A1631174301614&ei=nb45YY77JJvahwPZn6cY&oq=%EC%9D%B4%EB%A9%94%EC%9D%BC+%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D&gs_lcp=Cgdnd3Mtd2l6EAMyBQgAEIAEMgUIABCABDIFCAAQgAQyBQgAEIAEMgYIABAIEB46BwgjEOoCECc6BAgjECc6BAgAEEM6CwgAEIAEELEDEIMBOggIABCABBCxAzoECAAQA0oECEEYAFDjjQpY-agKYPOpCmgIcAB4BYABlwGIAbAZkgEEMS4yNZgBAKABAbABCsABAQ&sclient=gws-wiz&ved=0ahUKEwjOvcHmtfHyAhUb7WEKHdnPCQMQ4dUDCA4&uact=5


1. reset.scss & common.scss

reset.scss, common.scss의 위치는 index.js에서 한번만 작성한다.

  • reset.scss, common.scss는 기본적으로 index.js에서 import
  • index.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 변수를 renderreturn 사이에서 선언할 경우 컴포넌트가 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

참고) onChange 함수 합치기

8. vs

<a> tag는 사용하면 새로고침 하는 것처럼 html을 새로 다 받아 오는 반면,
<Link> 를 사용하면 컴포넌트만 바꿔줍니다.
렌더링 최적화를 위해서 <Link> 사용해주세요!

9. import 순서

  • 라이브러리
    • React 관련 패키지
    • 외부 라이브러리
  • 컴포넌트
    • 가까운 컴포넌트 to 먼 컴포넌트
  • 함수, 변수 및 설정 파일
  • 사진 등 미디어 파일(.png)
  • css 파일 (.scss)

좋은 웹페이지 즐겨찾기