ziglog

    Search by

    4월 첫주차 기록

    April 1, 2022 • ☕️☕️☕️☕️ 19 min read

    꽃가루를 날려~


    배워가기

    literal template 타입 활용 및 정의 방법

    분 단위를 문자열로 나타낼 수 있는 경우는 ‘00’ ~ ‘59’로 60가지 케이스가 존재한다. 이 60가지를 손수 타이핑하여 type을 지정하기에는 너무나 버겁다.

    이럴 때 literal template을 활용하여 간편하게 타이핑할 수 있다. 아래와 같은 코드로 타입을 정의하면 ‘00’~’59’ 까지 받을 수 있는 타입이 완성된다.

    Copy
    type ZeroToFive = "0" | "1" | "2" | "3" | "4" | "5";
    type ZeroToNine = ZeroToFive | "6" | "7" | "8" | "9";
    type minute = `${ZeroToFive}${ZeroToNine}`;

    git range cherry pick

    여러 개의 커밋을 cherry pick 하고 싶을 때, range cherry pick을 하면 된다.

    Copy
    git cherry-pick {cherry pick할 시작 커밋}^..{cherry pick할 마지막 커밋}

    cherry pick할 시작 커밋 뒤에 ^ 를 빼면, cherry pick할 시작 커밋 의 바로 다음 커밋부터 cherry pick이 된다.

    개발중 API 모킹이 필요할 때

    • 시간이 없다 → 더미데이터 사용(json | JS object)

      • 장점: 쉽다. await sleep(3000) 처럼 호출 지연이 포함된 자유로운 형태로 만들 수 있다.
      • 단점: 데이터를 fetching해오는 것이 아니라서 API 호출 환경과 완전 다르다. (함수를 호출하거나 import 해서 사용..)
    • 시간이 있다 → Mock 서비스워커 사용.

      • 장점: 네트워크 수준에서 요청을 인터셉트 해주기 때문에 실제 API 호출와 동일한 환경 구성이 가능하다. / restful 한 api 응답을 직접 구현할 수 있다. / 한번 구성해놓으면 테스트환경이나 스토리북에 통합해서 모킹할 수 있다.
      • 단점: 응답 핸들러를 작성할 때 걸리는 시간 + 러닝커브

    수동으로 프록시 설정하기

    개발중인 프론트의 origin 과 호출 할 API의 origin이 다를 경우 CORS 이슈가 발생할 수 있다. 이때 API 서버에서 응답 헤더에 매번 Access-Control-Allow-Origin을 설정해주는 것 보다 프록시를 수동으로 설정해주는 것이 리소스 절약에 도움이 된다.

    프록시 설정 방법

    • http-proxy-middleware: react-script 2.0.0 이상을 사용하는경우 express 인스턴스 미들웨어에 자체 프록시를 연결할 수 있다. 예시
    • package.json 에 proxy 추가 예시

    tsconfig lib의 역할

    tsconfig에 lib가 지정되지 않으면 target에 따라 기본 리스트가 삽입된다.

    • target ES5: [“DOM”, “ES5”, “ScriptHost”]
    • target ES6: [“DOM”, “ES6”, “DOM.Iterable”, “ScriptHost”]

    ScriptHost: Window Script Hosting System(WSH) API와 관련된 라이브러리다.

    예를 들어, lib: ["ES2020"]과 같은 식으로만 lib을 추가하면 DOM 관련 코드를 사용하는 곳에서 에러가 발생하여 타겟 버전으로 빌드가 안될 수 있다. 이때, lib를 처음 추가하면 기본적으로 제공되는 리스트가 덮어씌어지기 때문에 함께 넣어주는 것이 좋다.

    Copy
    lib: ["DOM", "ES5", "ES2020", "ScriptHost"];

    Next의 Link 태그는 child에 따라 처리해야할 일이 달라진다.

    • child가 단순 문자열이라면, <a> 태그로 감싸기만 하면 된다.
    • child가 styled-components 또는 emotion으로 만든 styled 컴포넌트라면 LinkpassHref prop을 추가해야 한다.
    • child가 함수 컴포넌트라면 해당 컴포넌트를 React.forwardRef로 감싸고, LinkpassHref prop을 추가해야 한다.

    Ref https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-custom-component-that-wraps-an-a-tag

    cypress 이것저것

    • cypress에서 input value를 가져오기 위해서는 invoke 메서드를 활용해야 한다.

      Copy
      .invoke('val')
      .then(value => {
        // 이 value 값이 input value
      });
    • cypress는 기본적으로 rtl에서 사용하는 getByText 같은 메서드가 없는데, 이때 cypress용 rtl을 설치하거나 아래와 같은 방식으로 구현할 수 있다.

      Copy
      cy.get("tbody").contains("배달상세페이지").click();
    • cypress에서 cy.get("body > div")cy.get("body").within(() ⇒ {cy.get("div")}) 으로 풀어쓸 수 있다.

    dynamic routing

    dynamic routing을 리터럴 그대로 활용할때는 (ex. 특정페이지를 찾거나 페이지가 맞는지 확인하는 로직) : 문자를 염두에 두고 코드를 작성해야 한다.

    Copy
    const route = routes.find((it) => {
      const compareTargetPath = it.path
        .split("/")
        .map((it_) => (it_.includes(":") ? ":id" : it_))
        .join("/");
    
      const compareLocationPath = `/${location.pathname
        .split("/")
        .filter((it_) => !!it_)
        .map((it_) => {
          if (!isNaN(Number(it_))) return ":id";
          return it_;
        })
        .join("/")}`;
    
      return compareTargetPath === compareLocationPath;
    });

    이때, react-router-dom의 matchPath를 활용하면 코드를 더 간략하고 직관적으로 작성할 수 있다.

    Copy
    return matchPath(location.pathname, { path: it.path, exact: true }) !== null;

    route config 한번에 관리하기

    각 페이지에 관한 route config 파일을 만들고 모든 페이지를 한번에 관리하면, 타입추론 및 공통화된 속성을 통한 코드 작성이 용이하다.

    Copy
    export interface RootComponentProps {
      readonly path: string;
      readonly name: string;
      viewport?: string;
      exact?: boolean;
    }
    
    export interface RouteConfigure<
      P extends { exact?: boolean; sidebar?: boolean }
    > extends Omit<RootComponentProps, "exact"> {
      readonly id: string;
      component: React.FC<P>;
      props?: Partial<P>;
    }
    
    export function createRouteConfigure<
      T extends RouteConfigure<P>,
      P = Pick<RootComponentProps, "exact">
    >(params: Readonly<T[]>): (RouteConfigure<P> & T)[] {
      return params as unknown as (RouteConfigure<P> & T)[];
    }
    const routes = createRouteConfigure([
      {
        id: "로그인",
        path: "/login",
        name: "로그인",
        component: LoginPage,
        props: {
          exact: true,
          sidebar: false,
          footer: false,
          spa: false,
        },
      },
      {
        id: "검증",
        path: "/authorize",
        name: "검증",
        component: AuthorizePage,
        props: {
          exact: true,
          sidebar: false,
          footer: false,
          spa: false,
          validation: false,
        },
      },
      // ...
    ]);

    CHANGELOG.md 작성하기

    standard-version 라이브러리를 사용하면 편하다.

    장점은 다음과 같다.

    • 커밋 컨벤션에 따라 CHANGELOG.md를 자동 작성해준다.
    • 자동 tagging을 지원한다.
    • git-flow 브랜치 전략과 잘 맞아 떨어진다.

    Ref https://musma.github.io/2020/07/27/changelog.html](https://musma.github.io/2020/07/27/changelog.html

    useQuery의 data를 다른 hook에서 사용하기

    useQuery의 data 값을 다른 hook에서 사용해야 하는 경우, data의 값이 최초 undefined일 때를 제외할 수 있는 최선의 방법은 무엇일까?

    1. useEffect를 사용해서 data 값이 변경되면 해당 hook의 값을 세팅한다. ➡️ useEffect의 사용을 줄여주는 게 react-query의 장점이기도 한데, 이를 살리지 못한다.
    2. useQuery 전용 HOC를 만들어서 data가 undefined가 아닐 때만 하위 컴포넌트를 렌더링한다. 이렇게하면 하위 컴포넌트가 사용하는 hook의 data는 반드시 undefined 가 아니다. ➡️ pending/fulfilled 상태를 구분할 수 있는 react-query의 장점을 굳이 HOC로 다시 구현하는 느낌이다.

    Placeholder and Initial Data in React Query 포스팅을 참고해봐도 좋을 것 같다.

    컴포넌트의 optional 타입

    컴포넌트의 타입을 선언할 때 optional 타입은 지양하는 것이 좋다. 실제 사용부에서 props로 값을 받을 때 optional로 받을지 여부는 컴포넌트의 관심사가 아니며, 인터페이스의 타입은 원본 데이터 타입을 그대로 유지하는 것이 좋다.

    optional을 사용한다면 필드 하나를 필수값으로 변경해도, 모든 사용부에서 수정이 필요해져 사용성이 굉장히 나쁘다.

    Copy
    // optional 사용
    export interface ButtonProps {
      id?: number;
      name?: string;
      disabled?: boolean;
      onChange: (value: string) => void;
    }
    
    // 사용부
    const CuteButton: VFC<ButtonProps> = ({ onChange }) => {};
    const NiceButton: VFC<ButtonProps> = ({ id, disabled, onChange }) => {};
    const GoodButton: VFC<ButtonProps> = ({ id, name, disabled, onChange }) => {};

    위 코드에서 만약 name 필드가 필수값이 되면, ButtonPropsCuteButton, NiceButton의 사용부에서 모두 수정이 필요하다.

    혹은 만약 title이라는 필드가 새롭게 필요해진다면? ButtonPropstitle을 받지 않았던 모든 사용부에서 수정이 필요하다.

    원본 데이터 타입은 유지하고 사용부에서 따로 타입을 선언해서 사용한다면, 값 하나를 필수값으로 변경한다거나 필드를 추가하는 등의 변경이 일어나도 사이드 이펙트가 적다.

    Copy
    // 원본 데이터 유지
    export interface ButtonProps {
      id: number
      name: string
      disabled: boolean
      onChange: (value: string) => void
    }
    
    // 무엇을 optional로 받을 것인지 여부를 각 사용부에서 결정하고 수정한다
    type CuteButtonProps = Partial<Omit<ButtonProps, 'onChange'>> & Pick<ButtonProps, 'onChange'>
    type GoodButtonProps = Partial<Omit<ButtonProps, 'name' | 'onChange'>> & Pick<ButtonProps, 'name' | 'onChange'>
    
    const CuteButton: VFC<CuteButtonProps> = (props) => {}
    const GoodButton: VFC<GoodButtonProps> = (props) => {}
    
    // 사용부
    <CuteButton onChange={() => {console.log('큐트')}} />
    <GoodButton id={3} name='버튼' onChange={() => console.log('좋아')} />

    위 코드에서는 ButtonPropstitle이라는 필드가 추가되어도 CuteButton, GoodButton에는 아무런 영향이 없다.

    또는 GoodButton에서만 name을 필수로 받고 싶다면? GoodButtonProps만 수정하면 된다. 이때 CuteButtonProps를 사용하는 CuteButton에는 사이드 이펙트가 없음.

    또 다른 방법을 살펴보자.

    보통 컴포넌트의 prop은 필수값들이 optional 값보다 적은 경우가 일반적이다. 위에서 작성한 것처럼 사용부에서 따로 타입 선언을 한다면, ButtonProps 프로퍼티를 모두 optional로 만들고, 확장하는 곳(CuteButtonProps)에서 필요한 prop들만 required로 바꿔주는 접근도 가능하다.

    이때 일부 프로퍼티를 optional ➡️ required로 또는 반대로 바꾸는 Utility Type을 만들어두면 유용하다.

    Copy
    type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
    type WithOptional<T, K extends keyof T> = Omit<T, K> & { [P in K]?: T[P] };

    [P in K]-?에서 -? 표시는, 해당 인터페이스에서 optional을 모두 빼주겠다는 의미다. (optional 필드들을 required 값으로 바꿔준다.)

    처음의 GoodButtonProps의 인터페이스는 아래와 같이 작성할 수 있다.

    Copy
    type GoodButtonProps = WithOptional<ButtonProps, "name" | "onChange">;

    undefined vs null

    JavaScript에서 undefined는 아직 값이 할당되지 않은 상태의 변수의 값을 나타낸다. let 키워드로 선언한 변수나 (값을 할당하지 않은 경우), 아무것도 반환하지 않는 함수의 리턴값이 undefined에 해당한다.

    반면 null은 ‘할당을 위한 값’이라고 할 수 있다. null은 JavaScript 엔진이 자동으로 할당하는 것이 아니라, 개발자가 직접 특정 변수가 비어있음을 나타낼 때 사용한다.

    typeof nullobject며, typeof undefinedundefined이다. nullobject 타입인 것은 JavaScript 설계 초기 단계에서의 실수라고 알려져있으며, type이 undefined인 것은 오로지 undefined뿐이다. (즉 undefined는 JavaScript에서 타입으로도, 값으로도 쓰인다)

    궁금했던 것은, 동치 연산(== 또는 ===)과 falsy 연산을 쓸 때 nullundefined의 계산이 항상 헷갈렸기 때문이다.

    Copy
    null == undefined; // true
    null === undefined; // false

    nullundefined는 공통적으로 ‘값이 없음’을 뜻하기 때문에, 느슨한 동치 연산 비교(==)에서는 true를 반환한다.

    하지만 nullobject 타입, undefinedundefined 타입으로, 서로 타입이 다르기 때문에 엄격한 동치 연산 비교(===)에서는 false를 반환한다.

    사실 대단히 어려운 문제도 아닌데 괜히 헷갈렸다. 원래 하다보면 가장 기본적인 것들이 헷갈리기 마련이라고 위로해본다.

    Ref https://medium.com/@paytonjewell/javascript-eli5-null-vs-undefined-f7112a2b72dd https://steemit.com/kr-dev/@cheonmr/js-operator

    Content-Type: boundary

    HTTP의 POST 메서드를 사용할 때 헤더 Content-Typeboundary라는 key값을 사용할 수 있다.

    Copy
    Content-Type: multipart/form-data; boundary=something

    boundary는 메시지 파트를 구분하는 역할을 하며, 메시지의 시작과 끝 부분도 나타낸다. 첫 번째 boundary 전에 나오는 내용은 MIME을 지원하지 않는 클라이언트를 위해 제공된다.

    네트워크를 통해 메시지 및 파일을 전송할 때 패킷을 한번에 보내지 않고 나눠서 보내게 되는데, 여러 부분(multipart)으로 나누려면 각 부분의 경계를 표시해야 하며, 이 경우 메시지 및 파일의 전송되는 내용의 구분자로 사용한다.

    boundary를 선택하는 것은 클라이언트의 몫이며, 보통 무작위의 문자를 선택해 메시지의 본문과 충돌을 피한다.

    Ref https://lena-chamna.netlify.app/post/http_multipart_form-data/ https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Content-Type

    TypeScript에서 useRef 사용하기

    DOM 요소에 직접 접근하거나, 리액트 컴포넌트의 리렌더링 시에도 유지되는 값을 사용하기 위해서 useRef hook을 사용할 수 있다. 이때 useRef를 TypeScript와 함께 사용한다면, useRef의 타입에 유의해야 한다.

    @types/react 레포를 보면 useRef는 3개의 정의가 오버로딩되어 있다.

    • useRef<T>(initialValue: T): MutableRefObject<T> 인자의 타입과 제네릭의 타입이 T로 일치하는 경우, MutableRefObject<T>를 반환한다. MutableRefObject<T>의 경우, 이름에서도 볼 수 있고 위의 정의에서도 확인할 수 있듯 current 프로퍼티 그 자체를 직접 변경할 수 있다.

    • useRef<T>(initialValue: T|null): RefObject<T> 인자의 타입이 null을 허용하는 경우, RefObject<T>를 반환한다. RefObject<T>current 프로퍼티를 직접 수정할 수 없다. currentreadonlyRefObject를 반환하기 때문이다.

    • useRef<T = undefined>(): MutableRefObject<T | undefined> 제네릭의 타입이 undefined인 경우(타입을 제공하지 않은 경우), MutableRefObject<T | undefined>를 반환한다. 이렇게 사용할 경우 useRef가 반환하는 객체를 DOM 요소의 ref 프로퍼티에 집어넣을 수 없다.

    Copy
    import React, { useRef } from "react";
    
    const App = () => {
      const inputRef = useRef<HTMLInputElement>();
    
      return (
        <div className="App">
          {/* 🚨 Type 'MutableObject<HTMLInputElement | undefined> is not assignable... */}
          <input ref={inputRef} />
        </div>
      );
    };

    따라서 DOM을 직접 조작하기 위해 프로퍼티로 useRef 객체를 사용할 경우, RefObject<T>를 사용해야하므로 초깃값으로 null을 넣어줘야 한다.

    Ref https://driip.me/7126d5d5-1937-44a8-98ed-f9065a7c35b5


    이것저것

    • 서버의 관심사는 데이터가 클라이언트에서 어떻게 렌더링되는지가 아니다. CRUD에 따라 DB의 데이터를 읽고 내려주는 역할을 할 뿐이다. 데이터의 가공과 조합은 클라이언트가 하되, 보안/용량/성능상의 이슈가 있는 경우에만 서버가 데이터를 가공해서 내려주는 것이 좋다.
    • CSS 의사 클래스 :focus-within은 포커스를 받은 요소를 포함하고 있는 요소를 나타낸다. form 안에 input 필드 중 하나가 focus 되었을 때 form을 컨트롤 할 수 있어 유용하다.
    • align-items의 값이 flex-start면 flex container 기준으로 시작점에 붙고, baseline이면 text의 밑줄 부분을 기준으로 정렬된다.
    • emotion에서 컴포넌트에 css Prop을 넘겨주면 props에 css가 아닌 className으로 들어가게 된다.
    • Javascript에서 숫자 타입은 정수와 소수를 모두 표현하고 있는데, Javascript Parser는 정수 숫자 뒤에 점이 있으면 소숫점 이하의 수가 표현될 것으로 기대한다. 따라서 100.toString()의 경우, (소수점) 다음에 숫자가 올 것으로 기대하였지만 갑작스레 문자열이 나타난 것으로 인식하여 오류를 발생시킨다. (100).toString()이나 100..toString()으로 쓸 수 있다.
    • [time picker], [time range picker], [date + time range picker] 와 같이 연쇄적으로 컴포넌트를 재사용하는 컴포넌트를 개발할 때, 요구 사항 분석을 작은 단위(time picker)부터가 아닌 큰 단위(date + time range picker)부터 추려나가는 것이 컴포넌트의 확장성에 좋을 수도 있다.
    • 서로 다른 VSCode window를 합치려면 mac 상단에서 Window → Merge All Windows 해주면된다.
    • 구글 크롬의 V8 엔진은 OKR의 결과물이다 (Object: 잡지를 넘기는 것처럼 웹을 빠르게 만들기)
    • BHAG란 크고 위험하고 대단한 목표를 의미한다 (Big Hairy Audacious Goals)
    • 카드목록 UI에서는 보통 이미지(thumbnail)가 상단에 배치된다.
    • 아토믹 디자인에 대한 고민 (링크)
    • TypeScript에는 Capitalize 내장 유틸타입이 있다.
    • react-query와 recoil은 옵저버 패턴을 사용한다. (react-query, recoil)
    • input이나 textarea 태그에서 글씨 영역을 선택(강조)하고 싶을 경우, select() 메소드를 사용하면 영역이 선택된다.
    • 디자인 시안을 봤을 때, 사용성 측면에서도 확인해봐야 한다. 컴포넌트 시안만 봤을 때는, 기능상으로 문제가 없고 깔끔한 디자인으로 보일 수 있지만, 실제로 사용하는 측면에서는 여러가지 상황이 발생할 수 있다.
    • 서버에서 open api를 bypass한다는 말의 의미는, 프론트에서 open api에 대해 요청해서 받은 응답 값을 서버에서 따로 정제 없이 그대로 내려주겠다는 의미이다.
    • 외부 모듈을 import 할 때, 대소문자 구별을 잘 해야 한다. IDE가 자동으로 잡아주지 않는다. (webpackd을 사용하면 case-sensitive-paths-webpack-plugin 플러그인이 개발환경에서 잡아준다.)
    • 외부와 의존성이 높은 함수들이 사용되고 있는 함수는 테스트하기 힘들다. 의존성 높은 부분이 테스트할 부분이 아니라면 분리를 해버리고 통째로 mocking하는게 좋을 수도 있다.
    • 데이터를 모델링할 때 지향할 점들
      • 서버 요청 모델과 서버 응답 모델, 클라이언트 모델 인터페이스 모두 선언한다.
      • 응답 데이터와 클라이언트 모델 인터페이스를 매핑하는 함수를 만들어 활용한다.

    기타

    Core Web Vital

    Core Web Vital이 개발과 비즈니스 측면에서 중요한 이유와, 그 사례들을 소개하고 있다.

    Ref https://web.dev/vitals-business-impact/

    JavaScript에서 객체지향을 하는 게 맞나요?

    기술이란 지난 문제들을 해결하기 위해서 만들어졌기 때문에, 왜 이러한 기술이 필요했고 어떠한 발전 과정을 거쳤는지 파악하는 것이 중요하다.

    절차적 프로그래밍에서는 실행 순서를 강제로 바꾸는 것이 아니라 일정하게 반복되는 코드를 따로 만들어두고, 그에 해당하는 코드를 호출하고 나서 다시 원래 자리로 돌아오는 방식의 프로시저(함수)를 통해 개발이 진행된다. 즉, 데이터와 데이터를 처리하는 동작을 함수 단위로 코드를 분리하고 재사용하는 형태다.

    Copy
    var hp = 100
    var mp = 100
    
    gameloop:
    ...
    if (key == 'A') {
      goto magic
    }
    ...
    goto gameloop
    
    magic:
    mp -= 10
    ...
    goto gameloop

    그러나 절차적 프로그래밍은 기본적으로 전역 변수를 사용하기 때문에, 코드의 규모가 커질 때 골치가 아파진다는 문제가 있다. 서로 연관이 있는 데이터들을 하나로 묶어 namespace처럼 관리하여 해당 변수에 접근을 할 수 있는 구조체라는 형식이 탄생한다.

    Copy
    var character = {
      name: "teo.yu"
      hp: 300
      mp: 500
    }
    
    function useSkill(character) {
      ...
      character.mp -= 100 // 변수를 직접 수정하게 됨.
    }
    
    do_something(character)

    의미 있는 단위로 변수들을 하나로 묶음으로써 변수 명의 중복을 줄이고 함수나 배열 등에서도 하나의 변수처럼 활용할 수 있게 되었다.

    이렇게 데이터를 중심으로 코딩하는 것의 유용성이 퍼지게 되며 객체지향 프로그래밍이 등장하였고, 구조체와 항상 쓰이는 함수들을 하나로 묶어서 구조체와 함께 함수까지 포함하는 개념을 만들어 이를 class라고 불렀다.

    Copy
    // class
    class Character {
      name = "teo.yu"
      hp = 300
      mp = 500
    
      attack() {...}
      useSkill() {...}
      moveTo(toX, toY) {...}
    }
    
    // object
    var character = new Character();
    character.attack();
    character.useSkill()
    character.jump();

    서로 독립적으로 작동하는 작은 부분들을 조립하고 결합하는 방식이 되었고, ClassObject의 개념이 등장한다. 이런 식으로 작은 문제를 해결하는 것들을 모아서 하나의 문제를 해결하는 프로그램으로 개발하는 방식을 Bottom-up 방식이라고 한다.

    이렇게 프로그램을 객체로 바라보는 관점으로 프로그래밍을 하는 것을 Object-Oriented Programming (OOP) = 객체지향 프로그래밍 이라고 부르게 되었다.

    🤓 뒷 이야기는 👇 아래 링크에서~

    Ref https://yozm.wishket.com/magazine/detail/1396/?fbclid=IwAR15xqxPTnnP9Xh1BsH_i75ZDpvgoUt_IiFkk5vbnOfJ08rUo9zZfdC5U2w

    React 18 공식 문서

    가 나왔당. React와 함께 그 역사를 지켜보며 쑥쑥 자라나는 기분

    Ref https://reactjs.org/blog/2022/03/29/react-v18.html?fbclid=IwAR1TA5jkN2h02JpfTPXjWGrhWLWV-8JLGZkgGLJiTxKjqX-ZBX5QgwNhpwo

    NodeJS로 객체지향의 원칙을 지키면서 시스템 구성하기

    Ref https://nodejs.myeongjae.kim/

    CLI 자동완성을 지원해주는 툴

    Ref https://fig.io/

    실수한 명령어를 자동완성해주는 툴

    Ref https://github.com/nvbn/thefuck

    일 잘하는 개발자란?

    ‘주어진 업무를 수행할 때, 업무의 목표가 무엇인지 정확히 이해하고 수행하는 사람’!

    철학자 드라이퍼스 (아무리 봐도 ‘드레퓌스’가 더 자연스러운데…)가 제안한 기술 습득의 5단계 모델을 소개하고 있다. 철학자들은 정말 별걸 다 분석한다!

    드라이퍼스 모델이 5단계의 구분에서 제안하는 중요한 요소는 바로 직관 이라는 요소다. 인간은 구체적인 경험으로부터 직관을 얻을 수 있고, 이것은 컴퓨터와 같은 기계로 추론해 내는 것은 대단히 힘들다는 것이다. 소위 말하는 ‘짬’이 아닐까 생각한다.

    무슨 느낌인지는 알겠다. 아직 Competent 단계까지 가기에도 멀리있는 것 같지만, 한 단계씩 조금씩이나마 성숙해지기 위해 부단히 경험을 쌓아나가야 할 것이다.

    Ref http://blog.lastmind.io/archives/593


    마무리

    가구들이 하나 둘 도착했다. 독립하면 바꾸겠다고 했는데, 2년이라는 시간은 기다리기에 꽤나 긴 것 같다. 아직 다 꾸미진 않았는데, 지금까지도 맘에 든다. 일단 물건들을 정리하면서 많은 것들을 갖다 버린 게 홀가분하다.

    회사에서는 2월부터 함께 해왔던 컴포넌트 하나..가 아니고 두 개를 드디어 내보냈다! 나가자마자 버그가 있어서 핫픽스했지만 ㅠ 별건 아니지만 그래도 뿌듯한 기분이다.

    주말엔 전주여행을 다녀왔다. 올해 처음이기도 하고, 정말 오랜만의 여행인 것 같다… 작년 6월 이후 거의 1년만이다. 😵 날씨는 따뜻했지만 사람이 말도 안되게 많았고, 벚꽃은 아직 덜 폈다. 의도치 않게 먹짱 여행을 하고왔다. 다음 일주일은 좀 굶어야겠다.


    Relative Posts:

    4월 2주차 기록

    April 9, 2022

    3월 4주차 기록

    March 25, 2022

    zigsong

    지그의 개발 블로그

    RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon