제어의 역전 (Inversion Of Control)

  • 외부의 API에서 실행 되어야 하는 작업을 사용자(프로그래머)에게 넘기는 것

filter라는 메서드를 구현한다고 했을 때, 아래와 같이 구현을 해본다고 하자.

function filter(array) {
  let newArray = []
  for (let index = 0; index < array.length; index++) {
    const element = array[index]
    if (element !== null && element !== undefined) {
      newArray[newArray.length] = element
    }
  }
  return newArray
}

// 사용처
filter([0, 1, undefined, 2, null, 3, 'four', ''])

만약 위의 코드에서 0만 필터링 해주는 기능을 추가한다고 하면 어떨까? filter 메서드에 매개변수를 추가하고, 내부의 분기문을 하나 더 추가하고 하며 점점 더 filter 함수가 비대해진다. 이렇게 비대해지는 함수는 유지보수에 취약하다.

🧵 제어의 역전 사용해보기

function filter(array, filterFn) {
  let newArray = []
  for (let index = 0; index < array.length; index++) {
    const element = array[index]
    if (filterFn(element)) {
      newArray[newArray.length] = element
    }
  }
  return newArray
}

filter(
  [0, 1, undefined, 2, null, 3, 'four', ''],
  el => el !== null && el !== undefined
)

위와 같이 구현하게 된다면 filter 함수는 여러 요구사항에 맞춰서 대응될 필요가 없다. 즉 필터링하는 책임을 filter메서드에서 제어하는게 아니라, 사용처에 위임한 것이다. 그래서 제어의 역전이다.

  • 리액트에서 컴파운드 컴포넌트 역시 제어의 역전의 예시이다.
    • 컴파운드 컴포넌트 패턴 역시 하나의 컴포넌트에서 모든 책임을 제어하는게 아니라, 외부에 책임을 위임시켜버림으로써 많은 유즈케이스에 대응 할 수 있다.

그렇다고 무조건 제어의 역전이 정답일까? 언제나 그렇듯 과한 추상화는 경계하자. 유즈케이스가 많아질 때 추상화를 해도 늦지 않다.

ref - https://kentcdodds.com/blog/inversion-of-control


테스트코드 - 중첩을 지양하기

describe('테스트', ()=> {
    beforeEach(()=> {
     // 어쩌구..
    });
    describe('더 자세한 테스트', () => {
        beforeEach(()=> {
          // 어쩌구..
        });

	    it('더 더 자세한 테스트' () => {
            // 어쩌구... 
        });
    })
})

중첩된 테스트를 피해야하는 이유

  • 주로 테스트가 중첩되는 이유는 beforeEach 와 같은 셋팅을 공유해서인데,
  • 이렇게 셋팅을 공유하다보면.. 내부에 있는 it 절의 테스트 코드에서 실제로 어떤 변수를 참조하는지 알기 위해 코드를 계속해서 추적해야한다.
  • 또한 beforeEach 절을 사용하게 되면 어쩔 수 없이 변수의 재할당도 일어나는데, 이는 대표적인 안티패턴이다.

그러면 어떻게 해야할까? 인라인으로 작성하자.

  • 즉, 중첩된 테스트들을 분리하여 각각의 테스트 내부에서 셋팅들을 공유하자.
  • 즉, 각 테스트 그룹에 국한된 셋업들을 분리하여 작업하고 있는 코드에 대한 인지부하를 줄이자.

ref - https://kentcdodds.com/blog/avoid-nesting-when-youre-testing


테스트코드 - 세부구현은 테스트하지 않기

The more your tests resemble the way your software is used, the more confidence they can give you.

세부구현은 사용자가 알 수 없는 영역을 의미한다. 여기서 사용자란 개발자가 될 수도 있고, 실제 유저가 될 수도 있다.

세부구현을 테스트했을 때의 가장 큰 문제점은 아래와 같다.

  1. 리팩토링 했을 때에도 테스트가 실패 할 수 있다.
  2. 반대로, 코드의 로직이 변경되어 정상적으로 동작하지 않음에도 테스트가 성공 할 수 있다.

테스트코드는 동작에대해서만 테스트해야하지, 어떻게 구현 되었는지에 대해서 테스트해서는 안된다고 생각했다.

ref - https://kentcdodds.com/blog/avoid-the-test-user


Mist CSS 맛보기 😶‍🌫️

CSS in JS의 시대에서 Js from CSS라니! 따근따근한 프로젝트 궁금해서 한번 써봤당. 일반 css 문법을 사용하니 따로 러닝커브가 없다.

아래와 같이 mist.css 파일을 만들어준다.

  • css의 @scope 문법을 사용하여 해당 class 내의 요소에만 css 를 적용한다.
  • 기본적으로 data-attribute 기반으로 스타일 배리언트를 처리해준다.
@scope (.button) {
  button:scope {
    /* Default style */
    font-size: 1rem;
    border-radius: 0.25rem;

    &[data-size="lg"] {
      font-size: 1.5rem;
    }

    &[data-size="sm"] {
      font-size: 0.75rem;
    }

    &[data-danger] {
      background-color: red;
      color: white;
    }
  }
}

그리고 위와 같이 완성된 코드를 npx mistcss 로 빌드를 해주면

// Generated by MistCSS, do not modify
import './Button.mist.css'

type Props = {
  children?: React.ReactNode
  size?: 'lg' | 'sm'
  danger?: boolean
} & JSX.IntrinsicElements['button']

export function Button({ children, size, danger, ...props }: Props) {
  return (
    <button {...props} className="button" data-size={size} data-danger={danger}>
      {children}
    </button>
  )
}

짜잔 🤩 리액트 컴포넌트가 나온다.

  1. css 파일의 @scope내부 클래스는 프로젝트 내에서 유니크 한 값이어야한다. screenshot

    • @scope 내부에 주입한 클래스가 해당 컴포넌트의 className으로 주입되어있다. 그 이유로 유니크여야한 값이어야하는 것 같다.
    • 요 부분은 라이브러리가 고도화 되면 강제되는 방향으로 발전하게 될 것 같다.
    • 따라서 별도로 className 주입은 불가능하다.
  2. 컴포넌트의 props 들을 data-attribute로 주입한다. 따라서 css 코드에서 별도의 변수삽입 필요 없이 data-attribute만으로 스타일링 분기처리를 해줄 수 있다.

  3. 순수 css 코드만을 사용 할 수 있다보니 css의 이점을 잘 살릴 수 있다.

  • 일단 zero config여서 너무 편하다.
  • Data attribute를 prop으로 만들어준게 신기하다.
  • 안정화가 된다면 간단한 UI 개발시에는 잘 써먹을 수도 있을 것 같다.

아무튼 정말 신박한 아이디어가 아닐 수 없다..😵


오렌지 주스 테스트

  • 무리한 요구를 받았을 때 무조건 Yes or No 라고 대답하지 않기.
  • 이를 위해 비용이 얼마나 필요하고, 대안은 이렇다 저렇다 대답하기 🍊

기획자의 ‘이거 가능한가요?‘라는 질문에 yes 라는건 실제로 구현 가능여부라기보다는 주어진 일정과 상황 내에서 ‘내’가 감당 가능한지 여부이다.

ref - https://johngrib.github.io/wiki/orange-juice-test/