들어가며
destructing assignment 가 ES6 에 추가되었다는데.. JS를 시작한지 얼마안된 사람들은 도대체 그 기능을 어디다 써야 하는지 알수가 없다. 사실 대다수의 새로운 기술은 다량의 삽질과 고통을 줄이기 위해 탄생되었다. 그래서 어떤 기술에 대한 이해를 위해 일정량의 고통이 필요하다. 나 같은 초보자의 경우 기술이 없었다면 있었을 고통을 알지 못한다. 이미 모든 것이 잘 돌아가는 세상에서 시작했기 때문이다. 필자는 이해를 위해 그 고통을 상상해보았다.
이슈: 함수에 많은 인자(argument) 를 전달해야 할 경우
apiRequest(
'/index',
'GET',
{ optiron : true },
["Content-Type: text/plain"],
function(response) { ... },
null,
true
)
- 인자가 많다. 그 많은 인자가 도대체 무슨 의미인지 알기 힘들다. 고통스럽다!
- 함수 선언부로 넘어가 어떤 parameter들이 정의 되어있는지 참고해볼수도 있지만 번거로운 일이다. 고통스럽다!!
- 이런 문제를 해결하기 위한 키워드로
named arguement
가 있는데, 주로python
,kotlin
에서 사용된다. 내 고통을 해결해줄 기술이 있다고?!
# 파이썬 named argument
from math import sqrt
def quadratic(a, b, c):
x1 = -b / (2*a)
x2 = sqrt(b**2 - 4*a*c) / (2*a)
return (x1 + x2), (x1 - x2)
quadratic(31, 93, 62) # 이렇게 함수를 호출할 수도 있지만
quadratic(a=31, b=93, c=62) # 이렇게 각 인자의 의미를 함께 표기해서 호출할 수 있다.
- 자바스크립트에서도 해당 기능을 사용할 수 있다. 다행이다
- 구현 모습이 같지는 않지만 목표는 동일하게 달성할 수 있다. 야호!!
- 구현을 위해 일단 자바스크립트의 object destructing 에 대한 이해가 필요하다. 이제 object destructing 이해를 위한 명분이 생겼다!!
Object destructing
아래 코드를 보면 object destructing 이 무엇인지 직관적으로 알 수 있다.
// e.g. Object destructing
var someObject = { a: 1, b: 2, c: 3 };
var { a: a, b: c, c: c } = someObject;
// 더 즐여서 아래와 같이 사용할 수 있다.
var someObject = { a: 1, b: 2, c: 3 };
var { a, b, c } = someObject;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
objectg destructing 을 이용하면 위에서 인자의 의미가 무엇인지 파악하기 어려웠던 문제를 효과적으로 해결할 수 있다.
// 함수 호출
apiRequest({ // 인자들이 {}로 감싸져있다. 함수에 객체를 전달하고 있다!!
endpoint: 'products',
method: 'GET',
getParams: { category: 3 },
headers: ["Content-Type: text/plain"],
callback: function(response) {},
timeout: 0,
authRequest: true
})
// 함수 선언부
function ({
apiRequest, //{}를 주의하라. 우리는 객체를 인자(parameter)로 받고 있다.
endpoint,
method,
getParams,
headers,
callback,
timeout,
authRequest,
}) {
//...
}
무엇이 달라졌는가?
- 전달인자(argument) 가 객체이다
- 객체 특성상 key, value 형태로 전달하는 값이 어떤 의미를 가지고 있는지 표현이 가능하다.
- 그 표현을 통해 코드를 읽기가 편하다.
default parameter 활용하여 조금 더 개선하기
사실 object destructing 과정에서 특정 값을 기본으로 설정할 수 있다. 아래 코드를 참고하자
var someObj = { a: 1, b: 2 };
var { a, b, c = 123456 } = someObj;
// a = 1
// b = 2
// c = 123456
위 코드를 참고하면 apiRequest 함수를 더 개선할 수 있다.
// 함수 선언부에서 기본적으로 쓰이게 될 값을 세팅해두고
function apiRequest({
endpoint,
method = 'GET',
getParams = {},
headers = ['Content-Type: text-plain'],
callback = () => {},
timeout = 0,
authRequest = true,
} = {}) {
//...
}
// 실제 함수호출할 때에는 필요한 내용만 인자로 전달받는다
apiRequest({
endpoint: 'products',
getParams: { category: 3 },
});
이것이 바로 named parameters(or named arguments)
라 불리는 패턴이며 우리는 이것을 javascript object 와 es6의 destructing 문법을 통해 구현할 수 있다.
return 값에 적용
- 해당 destructing을 리턴값에 적용하여 활용할 수도 있다. 다음 예제를 참고하자
function apiRequest({ ... }) { // 인자로 무언가를 받게될 것이다라는 표현을 ... 으로 표현했다.
var response, error, loading
response = error = loading = null
// ... 추후 작성될 로직에서 response, error, loading 값이 할당될 것이다.
return { // 객체로 리턴하는 것에 주목!!
response,
error,
loading,
}
}
// 추후 리턴된 객체를 destructing 하여 활용할 수 있다.
var { response, error, loading } = apiRequest({ ... })
정리: Object 를 인자(argument) 로 전달하면 좋은 점
- 전달하는 인자가 무엇인지 표시할 수 있다. 의미전달에 용이하다
- 순서가 필요없다. 객체는 배열과 다르게 순서와 무관한 자료구조이기에 인자 전달시에 순서를 고려하지 않아도 된다.
- default parameter 를 사용하면 호출시 불필요하게 많은 인자를 전달받지 않아도 된다.
- 해당 패턴을 함수 retrun에도 사용할 수 있다.
조심할 것
1. 그렇다고 인자를 엄청받는 함수를 마구 만들어도 된다는 걸까?
- 아니다. 함수가 인자를 많이 받아서 처리한다면 문제가 있는 것일 수 있다.
- 분명 많은 역할을 담당하는 함수일 확률이 높고 SRP(단일 책임의 원칙)에 위배된다.
- 인자를 많이 받는 함수라면 해당 함수를 하위 함수로 분리할 수 없는지 생각해보자.
- 그 고민을 해소하기 위해 Partial Application of Functions 를 읽어보는 것도 좋겠다.
2 단지 여러 패턴중 하나일 뿐이다.
- 인자를 하나만 받아도 되는 함수에 굳이 객체로 인자를 전달해야 할까? 아니다.
- 방법론은 맥락적이다. 맥락이 달라지면 다시 고민해야한다. 뭐든 해결하는 방법론은 없다.