Upgrade to Pro — share decks privately, control downloads, hide ads and more …

섬세한 ISFP의 코드 가독성 개선 경험

kakao
PRO
December 08, 2022

섬세한 ISFP의 코드 가독성 개선 경험

#코드가독성 #React

협업에서는 코드 가독성은 중요한데요,
섬세한 성격의 프론트엔드 개발자는 어떠한 점을 중점을 두고 가독성을 개선해보려고 노력했는지 사례를 살펴보겠습니다.

발표자 : coze.nutmott
카카오 엔터테인먼트의 FE개발팀 coze입니다. 저의 MBTI는 섬세한 성격인 ISFP입니다.

kakao
PRO

December 08, 2022
Tweet

More Decks by kakao

Other Decks in Programming

Transcript

  1. 서종만 Coze.Nutmott


    카카오 엔터테인먼트
    Copyright 2022. Kakao Corp. All rights reserved. Redistribution or public display is not permitted without written permission from Kakao.
    섬세한 ISFP의 코드 가독성 개선 경험
    if(kakao)2022

    View Slide

  2. 누가 이렇게 쓴거야?

    View Slide

  3. ISFP의 특징
    사람들에게 친절하다.
    의견 충돌을 피한다.
    타인의 감정에 지나치게 민감하다.

    View Slide

  4. 타인이 자신이 작성한 코드에 느낄 WTF에 몹시 괴로워한다.
    사람들에게 친절하다. 의견 충돌을 피한다. 타인의 감정에 지나치게 민감하다.
    WTF
    WTF
    Works That Frustrate
    ISFP의 특징

    View Slide

  5. 긴 서브루틴
    여러가지를 수행하는 함수
    올바르지 않은 네이밍
    일반적인 리팩토링
    중복 코드
    전역 변수
    고정된 크기의 스타일
    부작용을 유발 사용하지 않는 코드

    View Slide

  6. 긴 서브루틴
    여러가지를 수행하는 함수 올바르지 않은 네이밍
    ISFP의 가독성 개선
    중복 코드
    전역 변수 고정된 크기의 스타일
    부작용을 유발 사용하지 않는 코드
    ISFP의 가독성 개선
    유지보수 향상을 위한 노력
    일반적인 리팩토링
    이 부분이 남들에게 쉽게 이해가 안되면 어떡하지?

    View Slide

  7. 긴 서브루틴
    여러가지를 수행하는 함수 올바르지 않은 네이밍
    ISFP의 가독성 개선
    중복 코드
    전역 변수 고정된 크기의 스타일
    부작용을 유발 사용하지 않는 코드
    ISFP의 가독성 개선
    유지보수 향상을 위한 노력
    일반적인 리팩토링
    이 부분이 남들에게 쉽게 이해가 안되면 어떡하지?

    View Slide

  8. 잘 보이는 형태로 작성해보기
    정확한 단어 고르기
    사례로 살펴본 가독성 개선

    View Slide

  9. 서론


    정확한 단어 고르기


    다른 뜻을 가진 단어와 구분하기


    보다 구체적인 단어로 바꾸기


    정확하지 않아도 좋은 경우


    잘 보이는 형태로 작성해보기


    정리
    A = A’

    View Slide

  10. const data = await loadData();

    View Slide

  11. const data = await loadData();

    View Slide

  12. load fetch
    가져와서 싣다 가져오다
    fetch?

    View Slide

  13. const data = await loadData();
    이미 특정 공간에 싣는 것까지 (저장1) 완결
    data에 가져옴 (저장2)

    View Slide

  14. const data = await loadData();
    const data = await fetchData();
    가져오다

    View Slide

  15. const data = await loadData();
    const success = await loadData();
    가져온 뒤 싣다 (완결)

    View Slide

  16. const data = await loadData();
    const success = await loadData();
    가져온 뒤 싣다 (완결)

    View Slide

  17. 다음 중 데이터가 없을 때 최초 한 번만 바뀌는 변수는?
    const { isFetching } = useQuery(['todos'], fetchTodos);
    const { isLoading } = useQuery(['todos'], fetchTodos);

    View Slide

  18. const { isFetching } = useQuery(['todos'], fetchTodos);
    const { isLoading } = useQuery(['todos'], fetchTodos);
    isLoading
    isFetching isFetching isFetching isFetching
    불러옴 다시 불러옴 다시 불러옴 다시 불러옴
    다음 중 데이터가 없을 때 최초 한 번만 바뀌는 변수는?

    View Slide

  19. const { isFetching } = useQuery(['todos'], fetchTodos);
    const { isLoading } = useQuery(['todos'], fetchTodos);
    isLoading
    isFetching isFetching isFetching isFetching
    불러옴 다시 불러옴 다시 불러옴 다시 불러옴
    다음 중 데이터가 없을 때 최초 한 번만 바뀌는 변수는?

    View Slide

  20. get? query?
    test('should show “Username”’, () => {


    render();


    expect(screen.getByText('Username')).toBeInTheDocument();


    });
    test('should show “Username”’, () => {


    render();


    expect(screen.queryByText('Username')).toBeInTheDocument();


    });
    Username: _

    View Slide

  21. get query
    가져오다 질문하다
    get? query?

    View Slide

  22. get? query?
    test('should show “Username”’, () => {


    render();


    screen.getByText('Username');


    });
    test('should show “Username”’, () => {


    render();


    screen.queryByText('Username');


    });
    no text: _
    Error: No instances found
    = null

    View Slide

  23. get? query?
    test('should show “Username”’, () => {


    render();


    screen.getByText('Username');


    });
    test('should show “Username”’, () => {


    render();


    screen.queryByText('Username');


    });
    no text: _
    Error: No instances found
    = null
    가져오다: 결과를 당연히 가져올 것으로 기대
    질문하다: 결과는 없을 수도 있음

    View Slide

  24. query: 결과는 없을 수도 있지만 확인만 해볼 때
    test('should show “Username”’, () => {


    render();


    expect(screen.getByText('Username')).toBeInTheDocument();


    });
    test('should show “Username”’, () => {


    render();


    expect(screen.queryByText('Username')).toBeInTheDocument();


    });

    View Slide

  25. get: 가져올 대상은 필히 존재한다고 가정
    test('should show “Username”’, () => {


    render();


    expect(screen.getByText('Username')).toBeInTheDocument();


    });
    test('should show login form', async () => {


    render();


    const userNameField = screen.getByText('Username');


    await user.type(userNameField, ‘Coze');


    expect(userNameField).toHaveValue(‘Coze');


    });
    가져온 뒤 대상을 활용하기를 기대하지만 추가 행동이 없음
    가져온 뒤 대상을 활용
    Username: Coze_

    View Slide

  26. get: 가져올 대상은 필히 존재한다고 가정
    test('should show “Username”’, () => {


    render();


    expect(screen.getByText('Username')).toBeInTheDocument();


    });
    test('should show login form', async () => {


    render();


    const userNameField = screen.getByText('Username');


    await user.type(userNameField, ‘Coze');


    expect(userNameField).toHaveValue(‘Coze');


    });
    가져온 뒤 대상을 활용하기를 기대하지만 추가 행동이 없음
    가져온 뒤 대상을 활용
    Username: Coze_

    View Slide

  27. const FruitBox = fruit => (





    {fruit.name}











    );
    사과

    View Slide

  28. const FruitBox = fruit => (





    {fruit.name}











    );
    사과

    View Slide

  29. 컴포넌트 작성 - UI 명칭 이해하기
    App Bar
    Card
    Box
    Global Navigation Bar
    Local Navigation Bar

    View Slide

  30. 컴포넌트 작성 - UI 명칭 이해하기
    App Bar
    Card
    Box
    Global Navigation Bar
    Local Navigation Bar

    View Slide

  31. const FruitCard = fruit => (





    {fruit.name}








    );
    const FruitBox = fruit => (





    {fruit.name}








    );
    액자처럼 감싸는 요소만을 기대
    사과

    View Slide

  32. const FruitBox = fruit => (





    {fruit.name}








    );
    사과
    const FruitBox = children => (





    {children}





    );

    View Slide

  33. 첫 번째 검색 결과가 항상 하이라이트 되는 기능을 제거해주세요
    AS
    -
    IS TO
    -
    BE

    View Slide

  34. 첫 번째 검색 결과가 항상 하이라이트 되는 기능을 제거해주세요
    AS
    -
    IS TO
    -
    BE

    View Slide

  35. Select Search
    선택하다 찾다

    View Slide

  36. Select Search
    선택하다 찾다
    하나의 결과를 항상 선택한다 모든 결과를 찾는 것에 그친다

    View Slide

  37. import { Select } from ui/Select';




    size="small"


    value={novel}



    import { Search } from ui/Search';




    size="small"


    value={novel}



    View Slide

  38. 서론


    정확한 단어 고르기


    다른 뜻을 가진 단어와 구분하기


    보다 구체적인 단어로 바꾸기


    정확하지 않아도 좋은 경우


    잘 보이는 형태로 작성해보기


    정리

    View Slide

  39. 짜증난다
    “짜증이라는 감정에는 다양한 감정이 뭉뚱그려져 있잖아요” - 김영하, 대화의 희열
    소설가 김영하가 학생들에게 금지시킨 말

    View Slide

  40. 짜증난다
    “짜증이라는 감정에는 다양한 감정이 뭉뚱그려져 있잖아요” - 김영하, 대화의 희열
    소설가 김영하가 학생들에게 금지시킨 말

    View Slide

  41. if (expirationTime < PROMOTION_END_TIME) {


    return remainTime / totalTime


    }

    View Slide

  42. if (expirationTime < PROMOTION_END_TIME) {


    return remainTime / totalTime;


    }

    View Slide

  43. if (expirationDate < PROMOTION_END_DATE) {


    return remainDuration / totalDuration;


    }
    순서 t

    View Slide

  44. get (가져오다) extract (추출하다), parse (분해하다), aggregate (합치다)
    number (숫자) limit (제한이 되는 수), count (총계)
    change (변경하다) convert (변환하다),
    fi
    lter (거르다), override (덮어쓰다)
    changed (바뀐) dirty (더러운 = 수정이 이루어진)
    대체 단어

    View Slide

  45. get (가져오다) extract (추출하다), parse (분해하다), aggregate (합치다)
    number (숫자) limit (제한이 되는 수), count (총계)
    change (변경하다) convert (변환하다),
    fi
    lter (거르다), override (덮어쓰다)
    changed (바뀐) dirty (더러운 = 수정이 이루어진)
    대체 단어

    View Slide

  46. 서론


    정확한 단어 고르기


    다른 뜻을 가진 단어와 구분하기


    보다 구체적인 단어로 바꾸기


    정확하지 않아도 좋은 경우


    잘 보이는 형태로 작성해보기


    정리

    View Slide

  47. 항상 정확해야 할까?
    test('should convert seconds to days', () => {


    const MIN_TO_SEC = 60;


    const HOUR_TO_SEC = MIN_TO_SEC * 60;


    const DAY_TO_SEC = HOUR_TO_SEC * 24;


    convertSecondToText(3 * DAY_TO_SEC + 12 * HOUR_TO_SEC + 30 * MIN_TO_SEC).toEqual('3.5 days');


    });

    View Slide

  48. 항상 정확해야 할까?
    test('should convert seconds to days', () => {


    const MIN_TO_SEC = 60;


    const HOUR_TO_SEC = MIN_TO_SEC * 60;


    const DAY_TO_SEC = HOUR_TO_SEC * 24;


    convertSecondToText(3 * DAY_TO_SEC + 12 * HOUR_TO_SEC + 30 * MIN_TO_SEC).toEqual('3.5 days');


    });
    test('should convert seconds to days', () => {


    const MIN = 60;


    const HOUR = MIN * 60;


    const DAY = HOUR * 24;


    convertSecondToText(3 * DAY + 12 * HOUR + 30 * MIN).toEqual('3.5 days');


    });
    초를 분으로 환산하기 위한 승수 (multiplier)
    초를 분으로 환산하기 위한 승수 (multiplier)

    View Slide

  49. 항상 정확해야 할까?
    test('should convert seconds to days', () => {


    const MIN_TO_SEC = 60;


    const HOUR_TO_SEC = MIN_TO_SEC * 60;


    const DAY_TO_SEC = HOUR_TO_SEC * 24;


    convertSecondToText(3 * DAY_TO_SEC + 12 * HOUR_TO_SEC + 30 * MIN_TO_SEC).toEqual('3.5 days');


    });
    test('should convert seconds to days', () => {


    const MIN = 60;


    const HOUR = MIN * 60;


    const DAY = HOUR * 24;


    convertSecondToText(3 * DAY + 12 * HOUR + 30 * MIN).toEqual('3.5 days');


    });
    초를 분으로 환산하기 위한 승수 (multiplier)
    초를 분으로 환산하기 위한 승수 (multiplier)

    View Slide

  50. 항상 정확해야 할까?
    test('should convert seconds to days', () => {


    const MIN_TO_SEC = 60;


    const HOUR_TO_SEC = MIN_TO_SEC * 60;


    const DAY_TO_SEC = HOUR_TO_SEC * 24;


    convertSecondToText(3 * DAY_TO_SEC + 12 * HOUR_TO_SEC + 30 * MIN_TO_SEC).toEqual('3.5 days');


    });
    test('should convert seconds to days', () => {


    const MIN = 60;


    const HOUR = MIN * 60;


    const DAY = HOUR * 24;


    convertSecondToText(3 * DAY + 12 * HOUR + 30 * MIN).toEqual('3.5 days');


    });
    초를 분으로 환산하기 위한 승수 (multiplier)
    초를 분으로 환산하기 위한 승수 (multiplier)

    View Slide

  51. 서론


    정확한 단어 고르기


    다른 뜻을 가진 단어와 구분하기


    보다 구체적인 단어로 바꾸기


    정확하지 않아도 좋은 경우


    잘 보이는 형태로 작성해보기


    정리

    View Slide

  52. N형과 S형의 차이
    감각형 (S)
    직관형 (N)

    View Slide

  53. N형과 S형의 차이
    감각형 (S)
    직관형 (N)
    섬세하고 디테일한 부분에 집착하는 경향이 있다.

    View Slide

  54. 한 눈에 잘 들어오게 작성하려면?
    이 발표는 섬세한 ISFP의 코드 가독성 개선 경험이라는 제목으로 작성되었습니다. 작성자는 MBTI가 ISFP인 카카
    오 엔터테인먼트의 FE개발팀 소속 coze.nutmott입니다. 이 발표는 서론에서 ISFP의 특징을 언급하며 가독성 개
    선에 특히 주목하는 부분을 사례를 들어 설명합니다. 크게 두 가지 부분에 주목을 하고 있는데, 하나는 정확한 단어가
    무엇인지 고민해 본 부분이고 다른 하나는 잘 보이는 형태가 무엇인지 고민해 본 부분입니다. 정확한 단어를 고를 때
    다른 뜻을 가진 단어와 구분해 본 사례나 보다 구체적인 단어로 바꿔본 사례를 함수명이나 UI 컴포넌트를 예시를 들
    며 소개하고 있습니다…


    View Slide

  55. 모델을 사용해보자
    용어 정리
    표 목차
    페이지 하위 단원 제목
    서론 5 0 서론
    본론1 10 3 정확한 단어 고르기
    본론2 35 3
    잘 보이는 형태로 작성
    해보기
    정리 70 0 정리
    FE개발팀: front
    -
    end 개발팀


    우시아월드: 북미 웹소설 서비스.


    Coze: 카카오 엔터테인먼트에서 사용하는 영문 호칭


    “저는 카카오 엔터테인먼트의 FE개발팀에서 우시아월드 프
    로젝트를 맡고 있는 Coze입니다.”
    <목차>


    서론 —————————————— p9


    정확한 단어 고르기————————— p20


    잘 보이는 형태로 작성하기 —————— p31


    결론 —————————————— p55

    View Slide

  56. 서론


    정확한 단어 고르기


    잘 보이는 형태로 작성해보기





    목차


    용어 정리


    각주


    정리

    View Slide

  57. 조건문
    const type =
    exception


    ? undefined


    : condA


    ? 'A'


    : condB


    ? condC


    ? 'BC'


    : 'BD'


    : 'A';

    View Slide

  58. 플로우차트
    const type =
    exception


    ? undefined


    : condA


    ? 'A'


    : condB


    ? condC


    ? 'BC'


    : 'BD'


    : 'A';
    exception
    condA
    condB condC
    ‘A’



    아니오
    아니오
    아니오
    unde
    fi
    ned

    ‘BC’
    ‘BD’
    아니오
    ‘A’
    द੘

    View Slide

  59. exception condA condB condC type
    TRUE unde
    fi
    ned
    TRUE ‘A’
    TRUE TRUE ‘BC’
    TRUE FALSE ’BD’
    ‘A’

    View Slide

  60. let type = ‘A';


    if (exception) type = undefined;


    if (condA) type = ‘A';


    if (condB) {


    if (condC) type = 'BC';


    else type = 'BD';


    }


    const type =
    exception


    ? undefined


    : condA


    ? 'A'


    : condB


    ? condC


    ? 'BC'


    : 'BD'


    : 'A';
    플로우차트
    ->

    View Slide

  61. const type = (function () {


    if (exception) return undefined;


    if (condA) return 'A';


    if (condB && condC) return 'BC';


    if (condB && !condC) return 'BD';


    return 'A';


    })();
    exception condA condB condC type
    TRUE unde
    fi
    ned
    TRUE ‘A’
    TRUE TRUE ‘BC’
    TRUE FALSE ’BD’
    ‘A’
    즉시 실행 함수와 early return의 활용

    View Slide

  62. let str = '';


    switch (type) {


    case 'apple':


    str = 'ࢎҗ';


    break;


    case 'banana':


    str = '߄աա';


    break;


    default:


    str = 'ನب';


    }

    View Slide

  63. const FRUIT_MAP = {


    apple: 'ࢎҗ',


    banana: '߄աա',


    DEFAULT: 'ನب',


    }


    const str = FRUIT_MAP[type] || FRUIT_MAP.DEFAULT;
    apple ‘사과’
    banana ‘바나나’
    ‘포도’

    let str = '';


    switch (type) {


    case 'apple':


    str = 'ࢎҗ';


    break;


    case 'banana':


    str = '߄աա';


    break;


    default:


    str = 'ನب';


    }
    대응 관계가 일직선 상에 가깝게 위치

    View Slide

  64. 서론


    정확한 단어 고르기


    잘 보이는 형태로 작성해보기





    목차


    용어 정리


    각주


    정리

    View Slide

  65. 목차 모델을 코드에 적용해 본 사례
    <목차>


    서론 —————————————— p9


    정확한 단어 고르기————————— p20


    잘 보이는 형태로 작성하기 —————— p31


    결론 —————————————— p55

    View Slide

  66. z
    -
    index 순서 꼬임 사례
    <>




    style={{ zIndex: 900 }}







    style={{ zIndex: 1000 }}





    >
    <>




    style={{ zIndex: 3000 }}







    style={{ zIndex: 1000 }}





    >
    해당 요소의 z
    -
    index를 높여주세요
    900
    1000
    1000
    3000

    View Slide

  67. z
    -
    index 순서 꼬임 사례
    <>




    style={{ zIndex: 900 }}







    style={{ zIndex: 1000 }}





    >




    style={{ zIndex: 2000 }}
    모달이 가려지게 됨
    900
    1000 1000
    2000


    모달
    2000


    모달
    3000
    <>




    style={{ zIndex: 3000 }}







    style={{ zIndex: 1000 }}





    >




    style={{ zIndex: 2000 }}

    View Slide

  68. 목차 작성을 응용한다면?
    <>




    style={{ zIndex: 900 }}







    style={{ zIndex: 1000 }}





    >




    style={{ zIndex: 2000 }}







    style={{ zIndex: 9999 }}





    <목차>


    서론 —————————————— p900


    본론 —————————————— p1000


    결론 —————————————— p2000


    부록 —————————————— p9999

    View Slide

  69. 목차 작성을 응용한다면?
    <>




    style={{ zIndex: ZINDEX_USAGES.HEADER_DROPDOWN}}







    style={{ zIndex: ZINDEX_USAGES.HEADER }}





    >




    style={{ zIndex: ZINDEX_USAGES.MODAL }}







    style={{ zIndex: ZINDEX_USAGES.ALERT_SNACKBAR }}





    export const ZINDEX_USAGES = {


    HEADER_DROPDOWN: 900,


    HEADER: 1000,





    MODAL: 3000,


    ALERT_SNACKBAR: 9999,


    };

    View Slide

  70. 서론


    정확한 단어 고르기


    잘 보이는 형태로 작성해보기





    목차


    용어 정리


    각주


    정리

    View Slide

  71. 용어 정리를 코드에 적용해 본 사례
    FE개발팀: front
    -
    end 개발팀


    우시아월드: 북미 웹소설 서비스. 세계 최대 무협 위주의 아시아 판타지 웹소설 플랫폼.


    Coze: 카카오 엔터테인먼트에서 사용하는 영문 호칭


    “저는 카카오 엔터테인먼트의 FE개발팀에서 우시아월드 프로젝트를 맡고 있는 Coze입니다.”
    <용어>

    View Slide

  72. if (accessType === 'kakao') {


    return Array.from(data)


    .filter(item => !(item.sugar > 5000))


    .sort((a, b) => a.energy - b.energy);


    }
    의도를 드러내기

    View Slide

  73. const shouldDisplay = accessType === 'kakao';


    if (shouldDisplay) {


    const foods = Array.from(data);


    const healthyFoods = foods.filter(menu => {


    const isUnhealthy = food.sugar > 5000;


    return !isUnhealthy;


    })


    const calorieOrderedFoods = healthyFoods.sort((a, b) => a.energy - b.energy);


    return calorieOrderedFoods;


    }
    용어 정리: 표식 삽입
    if (accessType === 'kakao') {


    return Array.from(data)


    .filter(item => !(item.sugar > 5000))


    .sort((a, b) => a.energy - b.energy);


    }

    View Slide

  74. 서론


    정확한 단어 고르기


    잘 보이는 형태로 작성해보기





    목차


    용어 정리


    각주


    정리

    View Slide

  75. 각주 모델을 코드에 적용해본 사례
    나는 칠레산 양상추와 경상북도 포항에 위치한 양계장의 닭이 낳은 계란을 버무린 샐러드를 아침 식사로 먹었습니다.


    나는 서울특별시 강동구 아리수로 93가길에 위치한 학교에 갑니다.

    View Slide

  76. 나는 아침 식사1) 를 먹었습니다.


    나는 학교2) 에 갑니다.
    2) 서울특별시 강동구 아리수로 93가길에 위치
    1) 칠레산 양상추와 경상북도 포항에 위치한 양계장의 닭이 낳은 계란을 버무린 샐러드
    각주 모델을 코드에 적용해본 사례

    View Slide

  77. const handleNovelClick = () => {


    if (novel) {


    sendLog(Events.NovelClick)({ novel });


    }


    };


    const handleChapterClick = () => {


    if (novel && chapter) {


    sendLog(Events.ChapterClick)({ novel, chapter });


    }


    };








    ࣗࢸ {novel.name}





    ୀఠ {chapter.name}






    각주를 활용하여 로그 전송 코드를 개선해본 사례
    소설 A


    챕터 a

    View Slide

  78. const handleNovelClick = () => {


    if (novel) {


    sendLog(Events.NovelClick)({ novel });


    }


    };


    const handleChapterClick = () => {


    if (novel && chapter) {


    sendLog(Events.ChapterClick)({ novel, chapter });


    }


    };








    ࣗࢸ {novel.name}





    ୀఠ {chapter.name}






    소설 A


    챕터 a
    각주를 활용하여 로그 전송 코드를 개선해본 사례

    View Slide

  79. const handleNovelClick = () => {


    if (novel) {


    sendLog(Events.NovelClick)({ novel });


    }


    };


    const handleChapterClick = () => {


    if (novel && chapter) {


    sendLog(Events.ChapterClick)({ novel, chapter });


    }


    };








    ࣗࢸ {novel.name}





    ୀఠ {chapter.name}






    소설 A


    챕터 a
    각주를 활용하여 로그 전송 코드를 개선해본 사례
    각주만 남기는 형태로 바꿔본다면?

    View Slide

  80. export default function LogReport(Component) {


    const Observer = (args) => {


    return (




    onClick={e => {


    const target = e.target.closest(


    '[data-click-log]'


    );


    if (!target) return;


    const event = target?.getAttribute('data-click-log');


    handler(event, target);


    }}


    >








    );


    };


    return Observer;


    }
    클릭 이벤트를 모아서 처리하는 HOC 작성
    소설 A


    챕터 a
    챕터 b
    소설 B


    챕터 a
    챕터 b
    소설 Z


    챕터 a
    챕터 b



    LogReport

    View Slide

  81. const handleNovelClick = () => {


    if (novel) {


    sendLog(Events.NovelClick)({ novel });


    }


    };


    const handleChapterClick = () => {


    if (novel && chapter) {


    sendLog(Events.ChapterClick)({ novel,
    chapter });


    }


    };








    ࣗࢸ {novel.name}





    ୀఠ {chapter.name}






    각주를 활용하여 로그 전송 코드를 개선해본 사례



    ࣗࢸ {novel.name}





    ୀఠ {chapter.name}






    HOC의 handler에서 처리

    View Slide

  82. const handleNovelClick = () => {


    if (novel) {


    sendLog(Events.NovelClick)({ novel });


    }


    };


    const handleChapterClick = () => {


    if (novel && chapter) {


    sendLog(Events.ChapterClick)({ novel,
    chapter });


    }


    };








    ࣗࢸ {novel.name}





    ୀఠ {chapter.name}






    파라미터 전송은 어떻게?



    ࣗࢸ {novel.name}





    ୀఠ {chapter.name}






    novel, chapter 파라미터 전송이 누락됨

    View Slide




  83. {novel.name}


    >


    {chapter.name}










    data-click-param={novel}


    data-click-log={Events.NovelClick}>


    {novel.name}




    data-click-param={chapter}


    data-click-log={Events.ChapterClick}>


    {chapter.name}








    파라미터 전송: 각주에 정보를 더 기입하기
    ChapterClick 로그 전송
    novel, chapter 파라미터 취합

    View Slide

  84. export const extractParams = (el: HTMLElement) => {


    if (!el) return {};


    let paramEl = el;


    const params = {};


    for (let i = 0; i < 3; i++) {


    const paramsEl = paramEl.closest(‘[data-click-param]');


    if (!paramsEl || !paramsEl.parentElement) break;


    const params = paramsEl.getAttribute('data-click-param');


    Object.assign(params, JSON.parse(params ?? '{}'));


    paramEl = paramsEl.parentElement;


    }


    return params;


    };
    파라미터를 DOM의 data
    -
    attribute에서 취합

    View Slide

  85. const Novel = ({ novel, chapter }) => (




    onClick={() => {


    sendLog(Events.NovelClick)({ novel });


    }}


    >


    {novel.name}








    );


    const Chapter = ({ novel, chapter }) => (




    onClick={() => {


    sendLog(Events.ChapterClick)({ novel, chapter });


    }}


    >


    {chapter.name}





    );
    기존의 Props Drilling 문제
    chapter에서는 로그 전송을 위해


    novel의 정보도


    props를 통해 전달받고 있었음

    View Slide

  86. const Novel = ({ novel, chapter }) => (




    onClick={() => {


    sendLog(Events.NovelClick)({ novel });


    }}


    >


    {novel.name}








    );


    const Chapter = ({ novel, chapter }) => (




    onClick={() => {


    sendLog(Events.ChapterClick)({ novel, chapter });


    }}


    >


    {chapter.name}





    );
    기존의 Props Drilling 문제 해결
    const Novel = ({ novel, chapter }) => (




    data-click-param={novel}


    data-click-log={Events.NovelClick}>


    {novel.name}








    );


    const Chapter = ({ chapter }) => (




    data-click-param={chapter}


    data-click-log={Events.ChapterClick}>


    {chapter.name}





    );
    handler가 DOM에서 필요한 정보를 취합하기 때문에


    상위 요소의 값인 novel을 넘겨줄 필요가 없음

    View Slide

  87. ISFP의 가독성 개선 사례 정리
    더 잘 보이는 형태를 고려해본 사례
    좀 더 정확한 단어를 고려해본 사례
    목차 각주
    용어

    ࠺त೧ࠁ੉૑݅ ׮ܲ ੄޷ܳ ыח ױয ҳ࠙
    ੌ߈੸ੋ ױযܳ ҳ୓੸ੋ ױয۽ ؀୓
    ࠗ੿ഛೞ؊ۄب оةࢿ੉ જই૑ݶ ೲਊ

    View Slide

  88. ISFP의 가독성 개선 사례 정리
    더 잘 보이는 형태를 고려해본 사례
    좀 더 정확한 단어를 고려해본 사례
    ࠺त೧ࠁ੉૑݅ ׮ܲ ੄޷ܳ ыח ױয ҳ࠙
    ੌ߈੸ੋ ױযܳ ҳ୓੸ੋ ױয۽ ؀୓
    ࠗ੿ഛೞ؊ۄب оةࢿ੉ જই૑ݶ ೲਊ
    목차 각주
    용어

    View Slide

  89. ISFP의 가독성 개선 사례 정리
    더 잘 보이는 형태를 고려해본 사례
    좀 더 정확한 단어를 고려해본 사례
    목차 각주
    용어

    ࠺त೧ࠁ੉૑݅ ׮ܲ ੄޷ܳ ыח ױয ҳ࠙
    ੌ߈੸ੋ ױযܳ ҳ୓੸ੋ ױয۽ ؀୓
    ࠗ੿ഛೞ؊ۄب оةࢿ੉ જই૑ݶ ೲਊ

    View Slide

  90. E.O.D

    View Slide

  91. Q&A

    View Slide