객체 다루기

carrot·2022년 6월 21일
0
post-thumbnail

Shorthand Properties

객체에서 프로퍼티 정보를 표현할 때 축약할 수 있는 표현 방식

const firstName = "Carrot";
const lastName = "Wilted";

const carrot = {
	firstName,	// firstName: "Carrot" 형태를 축약
  	lastName,
  	printName () {	// printName: function () {} 형태를 축약
    	return `${this.firstName} ${this.lastName}`
    }
}

리덕스에서 combineReducer 함수를 통해 분리 작성해 놓은 여러 리듀서들을 불러와 합치는 작업을 수행하는데 이때 combineReducer 함수에서 인자로 받는 리듀서 형태 또한 축약할 수 있다.

import userReducer from "./userReducer"
import todoReducer from "./todoReducer"

const rootReducer = combineReducer({
	userReducer,
  	todoReducer,
})

Computed Property Name

{
  [expression]: value
}

계산된 프로퍼티명으로 ES2015부터 지원된 문법이다. 프로퍼티의 이름을 정의하는 새로운 방법이다.
객체 리터럴의 프로퍼티명 자리에 대괄호[]와 표현식의 조합으로 사용되며 expression의 실행 결과가 프로퍼티의 이름이 된다.

// react에서 useState와 change event에서 자주 사용하는 패턴
const handleChange = (e) => {
	setState({
    	[e.target.name]: e.target.value
    })
}

(Object) Lookup Table

배열 데이터 구조에서 키와 밸류로 나열되어 있는 구조? computed property name을 활용해서 불필요한 if문, switch 분기문을 줄일 수 있는 방법으로 데이터를 함수에서 분리하여 함수를 조금 더 유연하게 활용할 수 있도록 도움을 준다.

// if 혹은 switch문의 case가 많아져서 길어지는 경우
function getUserType(type) {
	switch(type) {
      case "ADMIN":
        return "관리자";
      case "INSTRUCTOR":
        return "강사";
      case "STUDENT":
        return "수강생";
      default:
        return "해당 없음";
    }
}
function getUserType(type) {
	const USER_TYPE = {
      ADMIN: "관리자",
      INSTRUCTOR: "강사",
      STUDENT: "수강생",
      UNDEFINED: "해당 없음"
    }
    return USER_TYPE[type] ?? USER_TYPE['UNDEFINED'];
}

USER_TYPE을 분리하여 상수로 관리하고 import해서 사용한다면 함수의 구조를 변경하지 않고도 추가적인 case들을 적용할 수 있다.

import USER_TYPE from "./USER_TYPE";

function getUserType(type) {
	return USER_TYPE[type] || USER_TYPE.UNDEFINED;
}

Object Destructuring

객체를 구조분해 하고 할당하는 방법.

// 객체 프로퍼티명으로 분해할 수 있다.
const user = { id: 1, name: "carrot" };
const { id, name } = user;

// 배열도 분해할 수 있다.
const [first, , third] = [1, 2, 3];

// api 요청에 대한 response 단계에서도 자주 사용한다.
export default (req, res) => {
	const { id, name } = req.body;
}

구조분해의 장점으로는 입력받는 프로퍼티의 순서에 의존하지 않아도 된다는 점과, 필수 프로퍼티와 옵션 프로퍼티를 구분할 수 있다는 점이다.

function User(id, name) {
  this.id = id;
  this.name = name;
}

const Carrot = new User(1, "carrot");

위 예제의 User 생성자 함수는 id와 name 값을 특정 순서로 입력받아야 의미 있는 User 데이터를 생성합니다.
구조분해 할당시 이와같은 순서에서 벗어날 수 있습니다.

function User({ id, name }) {
  this.id = id;
  this.name = name;
}

const Carrot = new User({name: "carrot", id: 1})

필수 프로퍼티와 옵션 프로퍼티를 나눌 수 있다.

function User(name, { id, email }) {
  this.name = name;
  this.id = id || -1
  this.email = email || ""
}

const Carrot = new User("carrot", {})

Object.freeze

객체의 원본을 변경하지 않도록 해주는 내장 메서드.

const STATUS = Object.freeze({
  PENDING: "PENDING",
  SUCCESS: "SUCCESS",
  FAIL: "FAIL"
})

// STATUS 객체의 프로퍼티 값을 수정하거나 추가하는 작업이 모두 막힌다.
STATUS.PENDING = "P-END-ING";
STATUS.NEW_PROP = "PRE-";
console.log(STATUS);	// {PENDING: "PENDING", SUCCESS: "SUCCESS", FAIL: "FAIL"}

객체가 동결되어 있는지 확인할 수 있는 내장 메서드는 isFrozen()

console.log( Object.isFrozen(STATUS) );	// true

freeze는 객체의 1단계 프로퍼티까지만 데이터를 동결해주며, 2단계 이상의 depth로 내려가는 데이터의 경우 동결되지 않으므로, 별도의 동결 처리를 거쳐야 한다.

Prototype 조작 지양하기

ES2015 문법에서 class가 지원되기 전 까지는 생성자 함수라는 것을 통해서 클래스를 구현했음.

function User(id, name) {
  this.id = id;
  this.name = name;
}
User.prototype.printName = function () {
  return "User Name : " + this.name
}

자바스크립트 명세서의 발전으로 class 문법을 지원함으로써 위와 같은 생성자 함수나 함수의 프로토타입에 메서드를 추가하는 코드는 보기 힘들어졌다. 때문에 프로토타입을 조작하는 일의 필요성 자체가 줄어들었다고 볼 수 있다.

hasOwnProperty

객체 내장 메서드인 hasOwnProperty는 해당 객체의 프로퍼티 값을 가지고 있는지 여부를 boolean 형태로 결과를 나타내 주는 함수이다.

const Carrot = {id: 1, name: "carrot"};
Carrot.hasOwnProperty("name");	// true;

hasOwnProperty는 오버라이드로 덮어씌워져 전혀 다른 메서드가 될 수 있으므로 이 경우 Object의 프로토타입에 접근해서 메서드를 호출하는 것이 안전하다.

Object.prototype.hasOwnProperty.call(Carrot, "name");

// 유틸 함수로 만들어 필요할 때 마다 호출해서 사용하는게 좋다.
export function hasOwnProp(targetObject, targetProperty) {
  return Object.prototype.hasOwnProperty.call(targetObject, targetProperty)
}

hasOwnProp(Carrot, "name");	// true

직접 접근 지양하기

Object Model에 접근할 수 있는 방법을 적절히 통제한다.

const user = {
  name: "carrot",
  email: "carrot@carrot.field.com",
  status: "wilted",
  isLogin: false,
  isValidToken: false
}

로그인이나 로그아웃 같은 일련의 과정에서 user 데이터를 변경해야 한다면 다음과 같은 방법으로 직접 접근을 통제할 수 있다.

// user 객체에 접근할 수 있는 함수들을 생성한다.
const setLogin = (bool) => user.isLoggin = bool;
const setValidToken = (bool) => user.isValidToken = bool;

// 객체에 직접 접근하는 것이 아닌 대신 접근하는 함수들을 이용한다.
const login = () => {
  setLogin(true);
  setValidToken(true);
}

const logout = () => {
  setLogin(false);
  setValidToken(false);
}

유저가 동적으로 접하는 영역은 login, logout 함수와 같은 메인 함수가 담당하고, 데이터를 변경하는 동작은 setLogin, setValidToken과 같은 함수가 담당한다.
동작하는 기능을 분리해 놓으면 디버깅도 쉬워지고, 로직을 변경할 때도 쉬워진다.

profile
당근같은사람

0개의 댓글