배경

프론트엔드 개발자 포지션으로 인터뷰를 할 기회가 있어서 참여했다가 코딩 테스트를 3번 하게 되었다. 그 중 한 문제가 알고리즘과 유사한 문제였다. 나머지 두 개는 리액트로 컴포넌트를 만들거나 스타일을 입히는 문제라 잘 진행을 했는데, 평소 관심을 두지 않던 분야의 문제를 받았고 이렇게 다시 정리해본다.

디자이너의 문제 해결 방식

디자이너는 미적인 선택을 할 때 주로 직관을 사용한다. 특정 이미지의 색깔을 변경할 때 판단의 도구가 되는 것은 바로 감각이다. 사진을 보정해야 하는 상황을 가정해보자. 디자이너는 포토샵과 같은 편집 툴을 이용해 명도/채도 값을 변화시키는데, 이 때 판단의 기준은 본인의 감각이다. 값을 조금씩 바꾸어 보면서 결과를 눈으로 바로 확인하고, 보정을 마친다. 물론 주관적이라고 할 수도 있다. 일정 수준 이상의 주관적인 감각이 바로 디자이너의 역량이 된다.

잘못된 코딩 습관

주제는 코딩 테스트인데, 왜 디자이너의 작업 과정 이야기를 했을까. 이게 바로 내가 고쳐야 할 문제 중 하나이기 때문이다. 과거 코딩을 처음 시작했을 때도, 인터뷰에서도 문제가 된 나의 코딩 습관이었다.

특히 3D 작업을 할 경우, 모델링을 랜더링할 때, 빛과 같은 설정 값을 변화시키고, 눈으로 확인하는 작업은 매우 익숙하다. 나 또한 디자이너로 보낸 오랜 시간 동안 이 과정을 익숙하게 받아들였다. 이는 사실 CSS 작업을 해야할 때는 조금 유리하게 작용하기도 한다. 진짜 문제는 프로그래밍 문제를 해결해야 할 때의 잘못된 습관이다.

코딩을 할 때, 특정 문제나 이슈를 해결해야 할 때 내 모습은 이렇다. 이 문제는 아무 생각없이, 의식하지 않고 코딩을 할 때 자주 발생하는데, 결과적으로 내 시간과 성장의 기회를 깍아먹는다.

  1. 문제를 해결해야 할 상황에 주어진다.
  2. 바로 키보드에 손이 간다.
  3. 이것 저것 함수를 만들어보고, 로그를 본다.
  4. 가끔 검색도 하고 코드를 카피해서 붙여본다.
  5. 해결이 안 되면, 또 키보드를 두드린다.
  6. 운이 좋으면 문제가 해결되겠지만, 정작 얻은 것은 없다.

위 과정은 마치 디자이너가 사진을 보정할 때의 과정과 유사하다. 나는 그렇게 느껴진다. 하지만 나는 개발자가 이래서는 안된다는 것을 분명히 알고 있다.

자바스크립트 코딩 문제

내가 받은 문제는 다음과 같다.(100% 정확하진 않지만, 기억 나는대로 내가 받은 문제를 떠올렸다)

/*
트리 구조의 엘리먼트가 있다. 2개의 선택자 문자열을 인자를 받는 함수를 통해
아래와 같은 결과를 콘솔에 출력해야 한다.

printStr('.c-2', '.c-3');
`Home. I feel Happy Today.`
*/
<div id="wrap">
    <div class="c-1">
        Hello
        <div class="c-1-1">Welcome</div>
        <div class="c-1-2">to my</div>
    </div>
    <div class="c-2">
        Home.
        <div class="c-2-1">
            I
            <div class="c-2-1-1">am</div>
        </div>
    </div>
    <div class="c-3">
        Happy
        <div class="c-3-1">Today.</div>
    </div>
    <div class="c-4">
        Thanks
        <div class="c-4-1">Guys!</div>
    </div>
</div>
function printStr(from, to) {
    // ...
}

let result = printStr('.c-2', '.c-3');
console.log(result);

처음에는 간단해 보였다. 나는 jQuery를 다룰 줄 아니까, 선택자로 텍스트를 뽑아서 로그 찍으면 되겠구나. 그리고 재귀 함수를 작성해야겠다.

재귀 함수도 쉽게 작성하지 못했지만, 선택자로 div 내의 텍스트와 일반 텍스트를 구별해야했기 때문에 뜻대로 안 되니 당황하기 시작했다. 당황스러운 상황에 들어가니 다시 습관이 도지기 시작했다. jQuery 문서를 이리저리 뒤져보다가 함수를 여러 개 만들기를 반복했다.


내가 당황해하자 지켜보시던 면접관님이 내가 풀 수 있는 쪽으로 유도해주셨다. 바로 키보드를 치려고 하지 말고, 차근이 생각해보라. 그리고 면접이 끝날 시간이 다가오니, 말로 해보자면서 지금 문제가 되는 부분을 해결해줄 수 있는 함수가 있다면 어떻게 할 것인가? 등의 질문으로 당황한 내 상황을 진정시켜주었다. 문제를 정확하게 풀진 못했지만, 다시 한 번 내 코딩 습관과 문제에 대한 접근 방법에 대해 고민할 수 있는 계기가 되었다.

면접관님은 이런 코딩 습관을 어떻게 해결할 것인지에 대해서 물어보았다. 나는 이미 내 코딩 습관에 대해 알고 있었고, 생각해본적도 많았다. 나는 대략 이렇게 대답했다.

습관의 힘이라는 책을 보면, 습관에는 3가지 단계가 있는데, 신호 - 반복행동 - 보상 이 과정을 반복한다. 문제를 정의하고 해결할 방법을 찾아가는 논리적인 사고 없이 키보드를 치는 내 코딩 습관에 대해서 이미 알고 있었고, 앞으로 내가 문제를 만났을 때 (신호), 바로 키보드에 손을 올리는 행동 (반복행동) 대신, 그 사이에 종이와 펜을 넣어 논리적으로 생각할 수 있는 단계를 인위적으로 넣어보겠다.

평소 개발자는 문제에 대해 논리적인 접근을 할 수 있어야 한다고 생각했지만, 테스트에서 나는 그런 모습을 보여주지 못했다. (오늘 날짜로 아직 결과가 나오지 않았고, 같이 일 해보고 싶은 곳이었지만 결과는 잘 모르겠다.)

다시 풀어보기

집으로 돌아가면서 너무 억울했고 자책하기도 했다. 난이도 높은 문제도 아닌데 내가 왜 못했을까. 왜 당황했을까. 그리고 집으로 돌아가서 문제를 떠올렸고, 내가 해결해야 할 문제와 상황을 다시 정의해보면서 문제를 풀기 시작했다.

내가 받은 문제는 두 가지를 묻는 문제였다.

  1. 자바스크립트에서 children과 childNode를 구분하는 것
  2. 재귀 함수를 작성하는 것

그리고

나는 재귀 함수로 children과 childNode의 텍스트를 전달해주는 함수 getText를 작성했다.

let allStr;

function getText(el, str) {
    allStr = str || '';
    const $el = $(el);
    const contents = $el.contents();

    contents.each(function(i, o){
        if ($(this)[0].nodeName === 'DIV') {
            getText(this, allStr);
        } else {
            let text = $(this).text().replace(/\n/gi, "").trim();
            if (text.length > 0) {
                allStr += `${text} `;
            }
        }
    })

    return allStr;
}

(지금 볼 때 좋은 구조는 아닌 것 같지만) allStr이라는 전역변수에 재귀로 뽑은 텍스트를 추가해주는 식이었다.

function printStr(from, to) {

    // 시작과 끝 인덱스를 저장할 변수 선언
    let fromIndex, toIndex;

    // wrap 내 모든 자식 dom을 가져온다
    let allDiv = $('#wrap').children();

    // 리턴할 문자열
    let returnValue = '';

    // from, to의 인덱스 찾기
    allDiv.each(function(i, o){
        let className = $(o).attr('class');
        if (from.substr(1) === className) {
            fromIndex = i;
        } else if (to.substr(1) === className) {
            toIndex = i;
        }
    })

    // 각 노드의 텍스트를 뽑아 문자열 업데이트
    allDiv.splice(fromIndex, toIndex).map(function(o){
        returnValue += getText(o);
    });

    return returnValue;
}

메인이 되는 함수 printStr 에서는 시작 지점과 끝 지점을 구분할 인덱스 변수를 만들었고, 배열에서 해당 범위의 문자열만 뽑아 리턴하는 구조로 작성했다.

결과

알게 모르게 디자인 베이스의 개발자가 꽤 많아 보인다. 웹에서는 접점도 많고 니즈도 분명히 있기 때문으로 보인다. 그러나 그런 사람들이 나와 같은 과정을 반복하는지 모르겠다. 나만 겪는 문제인지 확신할 수는 없지만, 나는 내 단점을 명확하게 인식하고, 습관을 바꾸고, 더 성장하고 싶을 뿐이다.