클린코드 자바스크립트(4) - 객체
1. 객체다루기
1) Shorthand Properties
- Shorthand Properties는 JS에서 객체의 key 와 value 가 동일한 경우, 간단히 작성할 수 있게 도와주는 기능을 말한다. 그리고 흔히 사용하는 ‘메소드’는 객체에 저장된 함수를 일컫는 말이다.
- Shorthand 적용 전 예시
// 변수(firstName, lastName)와 객체(person)의 value가 동일한 경우
const firstName = 'Dong Kyoo';
const lastName = 'Lee';
const person = {
firstName: firstName,
lastName: lastName,
getFullName: function () {
return `${this.firstName} ${this.lastName}`
}
}
console.log(person.getFullName()); // Dong Kyoo Lee
- Shorthand 적용 후 예시
const firstName = 'Dong Kyoo';
const lastName = 'Lee';
const person = {
firstName,
lastName,
getFullName: function () {
return `${this.firstName} ${this.lastName}`
}
}
console.log(person.getFullName()); // Dong Kyoo Lee
- 다음으로 Concise Method(간결 메소드)를 사용하여, 객체의 value가 메소드인 함수를 간결화할 수 있다.
const firstName = 'Dong Kyoo';
const lastName = 'Lee';
const person = {
firstName,
lastName,
// key가 함수명이 됨
getFullName() {
return `${this.firstName} ${this.lastName}`
}
}
console.log(person.getFullName()); // Dong Kyoo Lee
2) Computed Property Name
- Computed Property Name이란, 동적으로 계산되어 들어오는 값의 속성을 key, value로 사용할 수 있는 기능이다.
- 아래는 리액트의 코드인데, 지금까지 나는 event를 value로만 이용했지, key로 사용한적은 없었다. 어쨋든 이렇게 event로 전달된 값을 Computed Property Name이 사용된 예이다.
import { useEffect, useState } from "react";
const App = () => {
const [state, setState] = useState({
name: '',
password: '',
})
const handleChange = (e) => {
setState({
// onChange에서 넘겨받은 event는 계산되어 e로 받음
// 계산되어진 값(e)의 속성(.target.name)을 key로 사용
...state, [e.target.name]: e.target.value
});
};
useEffect(() => {
console.log(state);
// {name: '1234', password: 'ABCD'}
}, [state])
return (
<div>
<input value={state.name} onChange={handleChange} name="name" />
<input value={state.password} onChange={handleChange} name="password" />
</div>
)
}
export default App;
3) Lookup table
- Lookup table은 key와 value가 나열된 형태의 table이다.
- Lookup table 용어는 JS의 공식 용어는 아니지만, 분기를 객체로 관리할 수 있어 코드를 유지보수하기 편리해진다.
- Lookup table 적용 전
function getUserType(type) {
switch (type) {
case 'ADMIN':
return '관리자';
case 'INSTRUCTION':
return '강사';
case 'STUDENT':
return '수강생';
default:
return '해당 없음';
}
}
console.log(getUserType('ADMIN')); // 관리자
console.log(getUserType('학부모')); // 해당 없음
- Lookup table 적용 후
function getUserType(type) {
// case의 분기값을 key로 사용
// case의 리턴값을 value로 사용
const USER_TYPE = {
ADMIN: '관리자',
INSTRUCTION: '강사',
STUDENT: '수강생',
}
return USER_TYPE || '해당 없음';
}
console.log(getUserType('ADMIN')); // 관리자
console.log(getUserType('학부모')); // 해당 없음
- 위 코드를 아래와 같이 리팩토링할 수도 있다.
function getUserType(type) {
return (
{
ADMIN: '관리자',
INSTRUCTION: '강사',
STUDENT: '수강생',
}[type] || '해당 없음'
)
}
console.log(getUserType('ADMIN')); // 관리자
console.log(getUserType('학부모')); // 해당 없음
4) Object Destructing
- 앞에서 잠깐 다루었던 내용인데, 파라미터를 여러개 받아야하는 경우가 있다. 이 때, 파라미터를 나열하여 받으면, 함수를 호출 할 때, 파라미터의 순서를 반드시 고려해야하는 불편함도 있고 넘길 값이 없을 때, 골치아플 수 있다. 이러한 경우 파라미터를 객체 구조로 분리할당하면 편리하다.
- 파라미터를 객체 구조 분리할당 전 예시
const Person = (name, age, location) => {
this.name = name;
this.age = age;
this.location = location;
return `이름: ${name} / 나이: ${age} / 지역: ${location}`;
}
console.log(Person('홍길동', 17, '서울')); // 이름: 홍길동 / 나이: 17 / 지역: 서울
console.log(Person('홍길동', '서울')); // 이름: 홍길동 / 나이: 서울 / 지역: undefined
- 파라미터를 객체 구조 분리할당 후 예시
const Person = ({ name, age, location }) => {
this.name = name;
this.age = age; // 디폴트 설정
this.location = location;
return `이름: ${name} / 나이: ${age} / 지역: ${location}`;
}
console.log(Person({ name: "홍길동", location: "서울" })); // 이름: 홍길동 / 나이: undefined / 지역: 서울
- 파라미터를 필수값과 옵션값으로 분리한 예시
const Person = (name, { age, location }) => {
this.name = name;
this.age = age; // 디폴트 설정
this.location = location;
return `필수값: ${name} / 옵션값: ${age}, ${location}`;
}
const Options = {
age: 37,
location: "서울"
}
console.log(Person("홍길동", Options))
- 배열도 아래와 같이 구조분해할당이 가능하다.
const orders = ['First', 'Second', 'Third'];
const [first, , third] = orders
console.log(first); // First
console.log(third); // Third
// 위와 같은 방법도 있지만, 아래와 같이 배열을 객체처럼 사용하면 조금더 명시적으로 나타낼 수도 있다.
const orders = ['First', 'Second', 'Third'];
const { 0: first, 2: third } = orders
console.log(first); // First
console.log(third); // Third
- 리액트의 props도 구조분해할당을 사용한다.
// [리액트의 props 예시]
import { useEffect, useState } from "react";
const Welcome = ({ colorState }) => {
return(
<div style=>배경색</div>
);
}
const App = () => {
const [_colorState, _setColorState] = useState("red");
return(
// react의 props도 객체 구조 분해할당 예로 볼 수 있음
<Welcome colorState={_colorState} />
);
}
export default App;
5) Object.freeze
- Object.freeze는 Object의 value값을 동결할 때 사용하는 메소드이다. 하지만, Depth가 하나 더 추가된 value(깊은 복사)는 동결되지 않는다. (깊은 복사도 동결하는 방법은 나중에 기회가 생기면 다뤄보는 걸로… 왜냐면, 내가 라이브러리를 배포하거나 할 경우가 없을 것 같음.)
const STATUS = Object.freeze({
PENDING: 'PENDING',
SUCCESS: 'SUCCESS',
FAIL: "FAIL",
POWER: {
ON: "ON",
OFF: "OFF",
}
});
STATUS.PENDING = "STOP";
console.log(STATUS.PENDING); // PENDING, 변하지 않았음.
STATUS.POWER.ON = "OFF";
console.log(STATUS.POWER.ON); // OFF, Depth가 하나 추가된 경우(깊은 복사)는 프리징되지 않음.
6) 직접 접근 지양하기
- 데이터를 다룰 때에는 데이터에 직접 접근하여 사용하는 방식을 지양하자. 그리고, 기능을 나누어 사용하는 것이 데이터를 안전하게 다룰 수 있는 방법이며, 전체 기능을 구조적으로 이해하기에 도움이 된다.
- bad case: Login과 Logout함수에서 data에 직접 접근하여 사용하고 있음
const data = {
isLogin: false,
isValidToken: false,
};
const Login = () => {
data.isLogin = true;
data.isValidToken = true;
}
const LogOut = () => {
data.isLogin = false;
data.isValidToken = false;
}
- good case
const data = {
isLogin: false,
isValidToken: false,
}
// data에 대신 직접 접근
// 논리적으로 레이어를 하나 더 추가하여, data에 직접 접근하는 로직을 분리 함
const setLogin = (bool) => {
data.isLogin = bool;
}
const setLogout = (bool) => {
data.isLogout = bool;
}
// data에 간접 접근
const Login = () => {
setLogin(true);
setLogin(true);
}
const LogOut = () => {
setLogin(false);
setLogin(false);
}
7) Optional Chaining (선택적 체이닝)
- 점(.)연산자에서 key에 점진적으로 접근하는 방식을 체이닝이라고 한다.
- 실무에서 서버에서 받아오는 data를 검사할 때, 많이 사용한적이 있다. Optional Chaining(?.)을 사용하면,렌더링 단계에서 data가 유실되거나 받아오지 못할 때 오류 자체를 방지할 수 있다.
// 서버로 깊은 요소에 접근하여 반환하는 예시
const response = {
data: {
userList: [
{
name: 'Lee',
// info: {
// tel: '010-1234-5678',
// email: 'Lee@gmail.com'
// }
}
]
}
}
const _getUserEmailByIndex = (userIndex) => {
return response.data.userList[userIndex].info.email;
}
const getUserEmailByIndex = (userIndex) => {
return response.data.userList[userIndex].info?.email;
}
// console.log(_getUserEmailByIndex(0)); // 에러 발생, .info가 유실되어 오류 발생
console.log(getUserEmailByIndex(0)); // 에러가 발생하지 않고, undefined 만을 반환
다음에 다루게 될 것
- 함수
댓글남기기