[React] 함수 재사용

OROSY·2021년 10월 2일
1

React

목록 보기
24/27
post-thumbnail

♻️ 함수 재사용

코드를 잘 작성한다는 것은 무엇일까요? 이번에는 리액트 개발자로서 함수를 사용함에 있어서 함수를 재사용하는 리팩토링에 대해 알아보도록 하겠습니다.

단순히 코드를 작성하고 기능 구현에 그치는 것이 아니라 다른 개발자들이 봐도 잘 이해할 수 있는 코드, 유지보수가 쉬운 코드, 재사용이 가능한 코드 등을 작성하기 위해 열심히 고민해야하므로 이중의 일환으로 함수 재사용에 대해 살펴봅시다.

❓ 재사용 가능한 함수

함수란, 한 가지의 일을 수행하는 코드가 블럭으로 묶여있는 것을 말하며, 간단한 명령만으로 동일한 코드를 필요한 곳마다 반복해서 사용하지 않을 수 있게 만들어줍니다.

프로젝트의 복잡도가 증가할수록 새로운 기능을 추가하는 것보다 기존 코드의 유지 보수에 더 많은 리소스를 투입하게 됩니다. 프로젝트의 대규모화에 따른 코드 유지 관리 비용 증가 자체를 막을 수는 없지만 코드의 복잡도를 낮추어 비용을 최소화 할 수는 있습니다.

☀️ DRY (Don't Repeat Yourself)

중복 코드 줄이기 작업은 효율적인 코드 작성을 위한 주요한 방법 중 하나입니다. 우선, 중복되는 로직을 추출해 반복적으로 사용하게 되면 프로젝트의 코드량을 줄일 수 있습니다.

로직의 관리 포인트가 줄어든다는 말은 하나의 수정사항을 반영하기 위해 여러 코드를 반복적으로 수정하지 않아도 된다는 뜻입니다. 코드를 예시로 들어 생각해보겠습니다.

Before

// App.js
fetch("https://localhost:8000/proucts/1")

After

// src/config.js
export const API = "http://localhost:8000";

// App.js
fetch(`${API}/product/1`);

위와 같이 반복되는 상수들을 별도 파일로 분리하여 관리하는 것만으로도 한 군데만 바꾸더라도 모든 주소를 동일하게, 안전하게 수정할 수 있게 됩니다. 유지 보수 관점에서 훨씬 좋은 코드가 되겠죠?

🤪 KISS (Keep It Simple, Stupid)

그렇다고 중복되는 코드를 무작정 추출만 해서도 안됩니다. 로직을 추출할 때 '함수가 너무 복잡하지는 않은가?'하는 질문을 꼭 던져봐야 합니다.

각 함수는 가급적 단순하게 유지되어야 합니다. 하나의 함수는 가급적 하나의 일을 하는 것이 좋습니다. 한 함수의 너무 많은 역할이 뭉쳐있다면 오히려 제한적인 상황에섬나 쓰이게 되기도 합니다. 재사용하기가 어려워진다는 말이겠죠.

Before

// DON'T
const fourRuleCalculate = (event, a, b) => {
	const { innerText } = event.target;
	
	if (innerText === "+") {
		return a + b
	} else if (innerText === "-") {
		return a - b
	} else if (innerText === "*") {
		return a * b
	} else if (innerText === "/") {
		return a / b
	}
}

#### After
// DO
const sum = (a, b) => a + b
const minus = (a, b) => a - b
const multiply = (a, b) => a * b
const divide = (a, b) => a / b

줄여 쓰겠다고 가독성을 과하게 희생시키지는 않았는지 살펴보세요. 때론 조금 장황한 코드가 유지보수하기 좋을 수도 있습니다.

아래 두 경우 중 어느 쪽이 중간에 로직을 바꿔넣기에 좋을지 생각해보세요!

// DON'T
width = container > 960 ? (growWithContainer ? (container * 0.8) : 960) : container;

// DO
if (container > 960) {
  if (growWithContainer) {
    width = container * 0.8;
  } else {
    width = 960;
  }
} else {
  width = container;
}

위 원칙들이 절대적으로 따라야 하는 황금률은 아닙니다. 경우에 따라 원칙을 벗어나야 더 좋은 코드가 될 수도 있습니다. 하지만 대부분의 경우 위 규칙들은 여러분의 코드를 보다 읽기 좋고, 쓰기 좋게 만들어 줄 것이라고 생각합니다.

🧑‍💻 실전 예제

fetch 함수 반복되는 로직 줄이기

Before

fetch("http://localhost:3000", {
	method: "POST",
	body: JSON.stringify({
    id: "jt",
    pw: "1q2w3e4r!"
  })
})
	.then(res => res.json())
	.then(res => console.log(res))
	.catch(err => alert(err))

After

const onSucceed = name => {
  if (name === "phoneNum') {
    fetch(`${API}/users/identify`, {
      method: 'POST',
      headers: {'Content-type': 'application/json' },
      body: JSON.stringify({
        phoneNumber: form.phoneNum,
      }),
    })
      .then(res => res.json())
      .then(res => {
        if(res.MESSAGE === 'SUCCESS'} {
          alert('인증번호를 전송하였습니다.');
        }
      });
  }
  if (name === 'identNum') {
    fetch(`${API}/users/identify`, {
      method: 'PATCH',
      headers: {'Content-type': 'application/json' },
      body: JSON.stringify({
        identifyNumber: form.identNum,
      }),
    })
      .then(res => res.json())
      .then(res => {
        if(res.MESSAGE === 'SUCCESS'} {
          alert('본인인증 완료');
          history.push('/join/signup');
        }
      });
      .catch(() => alert('인증번호를 확인해주세요.'));
  }
};
...
<Btn onClick={() => onSucceed(data.btnName)} />

onSucceedBtn 컴포넌트를 클릭했을 때 인자로 전달되는 name 값에 따라 서로 다른 동작을 하는 함수입니다. 하나의 함수로 줄이려는 의도가 보입니다.

하지만 기능을 살펴보면 1) 휴대폰 인증번호 전송, 2) 본인확인 두 가지 기능이 하나의 함수에 합쳐져 있음을 알 수 있습니다. 그러면 하나의 함수가 하나의 기능을 할 수 있도록 로직을 분리해보겠습니다. (Keep It Simple, Stupid)

  • requestCertificationNumber : 휴대폰 인증번호 전송
  • validateSelf : 본인확인
const onSucceed = (name) => {
  if (name === 'phoneNum') {
    // 휴대폰 인증번호 전송
    requestCertificationNumber(form.phoneNum);
  } else if (name === 'identNum') {
    // 본인확인
    validate(form.identNum);
  }
};

const requestCertificationNumber = (phoneNumber) => {
  fetch(`${API}/users/identify`, {
    method: 'POST',
    headers: { 'Content-type': 'application/json' },
    body: JSON.stringify({ phoneNumber }),
  })
    .then((res) => res.json())
    .then((res) => {
      alert('인증번호를 전송하였습니다.');
  });
};

const validateSelf = (identifyNumber) => {
  fetch(`${API}/users/identify`, {
    method: 'PATCH',
    headers: { 'Content-type': 'application/json' },
    body: JSON.stringify({ identifyNumber }),
  })
    .then((res) => res.json())
    .then((res) => {
      if (res.MESSAGE === 'SUCCESS') {
        alert('본인인증 완료');
        history.push('/join/signup');
      }
  })
  .catch(() => alert('인증번호를 확인해주세요!'));
};

함수를 분리하고 나니 이전보다 onSucceed 함수의 목적이 분명해보입니다. 여기서 .json() 등 반복되는 fetch 관련 로직만 다시 한번 더 추출해보겠습니다! (Don't Repeat Yourself)

import { API } from "../config";
import { customFetch } from "../utils/api";

const onSucceed = (name) => {
  if (name === 'phoneNum') {
    // 휴대폰 인증번호 전송
    requestCertificationNumber(form.phoneNum);
  } else if (name === 'identNum') {
    // 본인확인
    validateSelf(form.identNum);
  }
};

const requestCertificationNumber = (phoneNumber) => {
  customFetch(
    API.VERIFICATION,
    {
      method: 'POST',
      body: JSON.stringify({ phoneNumber }),
    },
    {
      onSuccess: (res) => {
        if (res.MESSAGE === 'SUCCESS') {
          alert('본인인증 완료');
          history.push('/join/signup');
        }
      },
      onFail: () => alert('인증번호를 확인해주세요'),
    }
  );
};

const validateSelf = (identifyNumber) => {
  customFetch(
    API.VERIFY,
    {
      method: "PATCH",
      body: JSON.stringify({ identifyNumber }),
    },
    {
      onSuccess: (res) => {
        if (res.MESSAGE === "SUCCESS") {
          alert("본인인증 완료");
          history.push("/join/signup");
        }
      },
      onFail: () => alert("인증번호를 확인해주세요"),
    }
  );
};

// config.js
const BASE_URL = 'https://10.18.2.92'
const VERIFICATION = '/users/verification'
const VERIFY = '/users/verify'

const API = { VERIFICATION, VERIFY }

// utils/api.js
const customFetch = (
  endpoint,
  options = {},
  { onSuccess, onFail } = {}
) => {
  const opts = {
    method: 'GET',
    headerds: { 'Content-type': 'application/json' },
    ...options
  }

  return fetch(BASE_URL + endpoint, opts)
    .then(res => res.json())
    .then(res => onSuccess && onSuccess(res))
    .catch(err => onFail && onFail(err))
}

위와 같이 함수 재사용에 대해서 배웠습니다. 바로 위 코드는 실제로 자바스크립트의 객체, 함수 등의 문법을 잘 이해해야만 작성할 수 있는 코드라 생각합니다.

대부분의 코드는 이해되지만, 저 또한 아직까진 모두 완벽히 이해한 것이 아니라서 주말을 통해 함수 재사용에 대해서 한번 더 공부하려 합니다. 리액트에 조금 더 적응될 때까지 달려보겠습니다! 🏃‍♂️🏃‍♂️

profile
Life is a matter of a direction not a speed.

0개의 댓글