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 만을 반환

다음에 다루게 될 것

  • 함수

댓글남기기