ABOUT ME

세상 흥미로운 존재에 대한 기록

Today
Yesterday
Total
  • 원티드 12월 프론트엔드 챌린지 2차
    카테고리 없음 2023. 12. 9. 13:07

     

    비지니스 로직을 제압하는 개발자가 퇴근을 한다

     

    프론트에서 해드릴게요!


    - 의도 자체는 좋았으나 안좋은 결과로 귀결되는 마는 상황

    - 지옥으로 가는 길은 선의로 포장되어 있다.  (서양 속담)

    - 잠깐 멈춰서서 구조적인 원인은 없는지 생각해볼 필요가 있다.

    - 동료를 돕기 위해 가져온 백엔드 추천, 페이지네이션, 검색 로직을 프론트엔드에서 구현하는게 뭐가 문제인가?

    - 협업에 강한 개발자 아닌가?

    - 문제 1. 이름에 맞는 단순한 역할의 컴포넌트가 아닌 본질에서 벗어난 컴포넌트가 되어간다.

    - 문제 2. 웹 애플리케이션 뿐 아니라 앱에서도 동일한 로직을 구현한다면 다른 언어로 같은 로직을 다시 구현해야 함

    - 문제 3. 핵심 비즈니스 로직이 외부에 노출된다. (ex. 부당하게 할인을 챙겨갈 수 있음)

    - 정렬이 바뀌어야 할 필요가 있다면, 프론트에서 API 호출 시 쿼리 스트링을 사용한다.

    - 프론트/백엔드에서 비즈니스 로직을 둘 다 고쳐야하는 일을 없어진다.

    - 놀랍게도 이러한 혼란은 지금도 이런저런 스타트업, 특히 주니어만으로 구성된 팀에서 빈번히 일어나는 일이다.

    - 비즈니스 로직과 이 로직들에 대한 책임을 어떤 기준으로 나누고 어느 수준으로 격리할 것인지에 대한 고민

    - 이를 유난스러움으로 받아들이면 곤란하다.

    - 클린 아키텍처에서 'Hexagonal Architecture' (육각형 아키텍처) 제안했다.

    - 웹 프론트엔드는 도메인 핵심 코드로부터 가장 바깥쪽에 존재하는 '세부사항'으로서 존재할 것을 제안하고 있다.

    Hexagonal Architecture - 클린 아키텍처

    비지니스 로직이란?


    '비즈니스 로직'과 'UI 기능 구현'의 차이

    - 복잡한 UI에는 결제, 회원가입, 환불 요청 등의 버튼이 있을 수 있음

    - 버튼 자체는 단순한 UI 요소이지만, 클릭 이벤트에 여러 조건들이 연결됨으로써 복잡해질 수 있다.

    - 이러한 요구 사항들을 흔히 '도메인 로직 / 비즈니스 로직'이라고 부른다. 

    - 비즈니스 로직 1. 가격 할인 로직 (백엔드 할 일)

    - 비즈니스 로직 2. 재고 관리에서 품절된 상품 표시 및 예약 주문 옵션만 표시하기 (프론트 할 일)

    - 비즈니스 로직 3. 로그인 시 인증

    - UI 로직은 그 자체로는 특정 비즈니스에 구속되지 않고, 사용자와 시스템 간의 기본적인 상호작용을 위한 보다 일반적인 차원에서의 기능 구현이다.

    - 반면, 비즈니스 로직은 주로 특정한 상황이나 조건에 따라 다르게 동작하도록 설계된 말 그대로 어떠한 비즈니스에 맞춰진 작업 흐름이다. (호환성이 떨어져 가져다가 바로 적용하기 힘듬)

    - 대부분의 비즈니스 로직은 사실 백엔드 API와 DB 사이 어딘가에서 다뤄지게 되고, 또 그래야만 한다. (정보 유출 이슈)

    - 그럼에도 불구하고 프론트엔드에서 비즈니스 로직에 대한 이해도를 갖추고 있어야 하는 이유는, 비즈니스 로직을 알아보고 UI로부터 적절히 분리하기 위해서는 비즈니스 로직을 알아보고 백엔드 API단으로 적절히 격리하기 위해서라고 보아도 무방하다.

    - 비지니스 로직과 UI가 잘 분리되어 있는 제품을 설계할 수 있게 된다면 훨씬 더 확장성 있는 업무 환경을 갖출 수 있다.

     

    비즈니스 로직과 UI를 분리하기

    - 비즈니스 로직과 액션은 모두 어떠한 차원에서 '순수성'을 깨는 존재들이다.

    - 커스텀 훅도 사용하기 어렵지만 추상화를 위해서 나왔다.

    - '코드에서 가상의 레이어를 떠올리며 나누어 본다'라는 목표를 달성하기에 더 익숙하고 직관적이다.

    - 예제 코드에서 card로부터 많은 기능들이 분리되면서 기존 코드에 비해 수정의 이유와 시점이 서로 다른 코드들을 분리 / 격리하면서 작성하기 쉬운 형태가 됬다.

    - card는 이제 어떠한 비즈니스 로직도 포함하지 않으며 순수하게 카드에 담겨야 하는 title과 content를 렌더링 하는데 집중할 수 있게 되었다. CCP(Compound Component Pattern)를 활용하면서 isNewUser라는 flag도 card 외부로 빠지게 됐다.

    - 특히 VisibleTrigger 컴포넌트에 IntersectionObserver 관련한 로직이 캡슐화(Encapsulation)되고, 모든 보조 컴포넌트들이 card를 중심으로 묶여 높은 응집도(Cohesion)를 갖게 되었다.

    - CCP 패턴이 나오면서 React가 정말 발전 됐다. 유행한지 1년 정도 밖에 안됐다.

    - CCP 패턴의 정말 괜찮은 예시에는 radix-ui가 있다.

    - 좋은 코드는 좋은 회사에 있다. 하지만 좋은 코드에 너무 목말라 하지 말고, 할 수 있는 걸 다 해봐라

    - 추상화 수단으로 커스텀 훅만 알고 있다면, 다른 라이브러리에서는 추상화를 못할 텐니 패턴을 알아야 한다.

    - 응집도는 높을수록 좋고 결합도는 높으면 안좋음 (응고결저)

    물과 기름처럼 비즈니스 로직 격리하기


    격리의 기술 1장: '추출'과 '추상화'를 구분하라

    - 추출(Extraction)과 추상화(Abstraction)는 다르다.

    - 추출은 레몬즙을 레몬에서 물리적으로 분리해 내는 것과 같은 작업 (함수 쪼개기)

    - 추상화는 코드의 일부를 보다 일반화 된 형태로 개념화하는 과정이다.

    - 적절한 추상화가 거듭될 수록 코드 베이스는 점차 '간결(simple)'해진다.

    - 간결과 쉬움(ease)은 다르다. 사용하기 편하다고 다 좋은게 아니다.

    - 좋은 추상화를 통해 모듈성을 추구해 나갈 수 있다.

    - 모듈성(Modularity)이란 코드의 독립된 부분들, 즉 '모듈'들이 서로 잘 연결되고, 각각 독립적으로 기능할 수 있도록 하는 속성이다.

    - 모듈은 특정 기능을 수행하는 코드의 덩어리로서 각 모듈은 재사용 가능하고, 교체 가능해야 하며, 한 모듈의 변경이 다른 모듈에 미치는 영향을 최소화해야 한다.

    - 간결한 로직은 레고와 같고, 각 레고 블록은 하나의 '모듈'이 된다. 레고와 레고는 서로가 구체적으로 어떤 존재인지에 대해 알지 못한다. 단지 볼록한 부분과 오목한 부분이 서로 맞물린다는 규약만 공유할 뿐이다. 그외 추가적으로 고려할 필요가 없다.

    - 좋은 추상화란 코드 뒤에 숨은 의도를 파악하고 쓰임새에 맞도록 적절한 단위와 형태를 만들어 나가는 과정이다.

     

    격리의 기술 2장: '캡슐화'는 낄끼빠빠다

    - 추상화가 막막하다면 캡슐화(Encapsulation)을 고민해보자.

    - 캡슐화를 하다 보면 구조가 생기기 시작한다.

    - 캡슐화는 객체 지향 프로그래밍에서 중요한 개념으로 데이터(변수)와 그 데이터를 처리하는 함수(메서드)를 하나의 '캡슐' 내부에 담아서, 외부에서 직접적으로 접근할 수 없도록 하는 것을 말한다.

    - 캡슐화를 통해서 데이터의 무결성과 보안을 유지할 수 있으며, 외부의 영향으로부터 코드를 보호한다.

    - 캡슐화가 잘 되어 있는 코드는 기능별로 구분되어 있고 이해하기 쉬우며, 유지보수가 용이하다.

    - 캡슐화는 응집도(Cohesion)을 높인다. 응집도란 하나의 모듈이나 함수, 클래스가 얼마나 긴밀하게 관련된 기능들로 구성되어 있는지를 나타낸다.

    - 강한 응집도는 캠슐화된 코드 블록이 하나의 명확하고 관련된 목적을 가지게 한다. 이는 재사용성과 유지보수성을 높인다.

     

    격리의 기술 3장: '추상화 벽'과 '어댑터' 공법

    - 리팩토링을 해야할 때 무작정 옮기는 것이 아니라 옮기기 쉬운 형태로 만들어주고, 기존 동작을 담보해가면서 옮겨가는 것이 현명한 선택지이다.

    - 핵심은 추상화 벽과 어댑터이다.

    - 로직간 추상화 레벨을 맞춰주는 것이 좋다.

    - 위쪽은 추상화된 고수준이며 아래로 내려갈 수록 구체적(저수준)이다.

    쏙쏙 책 그림

    - 추상화를 통해 이전에 비해 고수준과 저수준의 로직이 분리되고, 같은 수준의 추상화 로직이 함께 실행된다.

    쏙쏙 책 그림 - new_function 추가

    - 한편 어댑터라는 개념은 소프트웨어 디자인 패턴 중 하나로, 호환되지 않는 인터페이스를 가진 두 크래스 또는 모듈을 연결해주는 역할을 한다. 이 패턴은 한 시스템에 다른 시스템과 상호 작용할 수 있도록 중간에서 인터페이스를 맞춰주는 일종의 '중재자'와 같은 역할을 한다.

    - 추상화 벽 사이에 어댑터를 둠으로써 격리하고 변경이 덜 되도록 설계를 해야 한다.

    - 어려운 개념이지만 이런한 것이 있다는 것을 인지하고 개발해나가는 것이 다음 단계로 나아갈 수 있다.

     

    과제

    - 본인이 작성했던 코드 중에서 비즈니스 로직을 분류해보고 해당 로직을 적절히 캡슐화하며 느낀 점 블로그에 정리하기

    - 1) 본인이 작성한 코드에서 비즈니스 로직을 표시해보기

    - 2) 비즈니스 로직에 해당하는 코드들을 오늘 수업에서 다룬 개념들을 활용하여 UI 로직으로부터 격리해보기

    - 3) 개선 전과 후에 어떤 차이가 있었는지 설명을 덧붙여보기

    - 비즈니스 로직을 분리하면서 어떠한 답답함이 느껴질 수 있는데, 다음 강의를 위해 미리 고민해 오기

     

    [아하! 모먼트 -02] 코드 바깥으로 벗어난 내가 바보처럼 느껴졌을 때

    - 단순한 HTML 수정 후 배포를 했는데, 중지되고 실패가 났다. 의존성 문제가 터졌는데 이상하다.

    - 코드 빌더와 코드 디플로이 사이의 AWS 에이전트가 죽어서 이벤트 리스닝 조차 못했다.

    - 재배포를 해도 통과 못했다.

    - 인스턴스를 죽이고 재시작을 하면 문제가 생길 것 같다. (2개의 인스턴스가 있는데, 1개는 실패한다.)

    - 인스턴스를 죽이면, 왜 에러가 났는지 로그가 없어진다. (이후에 같은 문제 파악 및 재발시 해결 못함)

    - 인스턴스를 죽여서... 로그가 날아가고 1대의 인스턴스로 돌리고 디버깅을 4명이서 해서 다음날 해결했음

    - 원인은 한 라이브러리가 별도의 공지 없이 마이너 업데이트에서 번들하는 ECMAScript 버전을 올려버린 것

    -  어떠한 이유로 패키지의 새로운 버전이 다운됬고, 최신 버전의 ECMAScript로 번들된 코드를 읽지 못한 실행 환경이 문제를 일으킨 것이다.

    - 테스트 환경에서는 되는데, 프로덕션에서 안되는 상황

    - 배포 전략(?) 

    - 기본 배포 전략은 롤링 업데이트이다. 쭉쭉 돌아가며 업데이트 되는 것이다. 한 대가 전체이다. 블루 / 그린 배포로 해결했다. 블루/그린 배포는 2개의 배포를 띄우는 것. (서버 비용은 더 들어감)

    - 모르면 당한다. (이번 건 처럼 배포 전략, CORS, 환경변수)

    - 서비스 내부에서 기능 코드만 작성할 것이니라, 배포를 못하고 운영을 못하면 안되기 때문에 관련된 기본 지식은 가지고 있어야 한다.

    - 그래서 한번 쯤 빌드 결과물도 열어보시라

    - 현대 웹 서비스의 많은 부분이 컨테이너 기반으로 돌아가기 때문에 도커는 언제든 공부해두면 무조건 도움이 됨

    - 당연히 모든 기술들을 다 익히고 있을 수 없음

    - 포지션이 나뉘었다고 해서 코드 외적인 환경에 대한 지식의 중요성이 떨어지는 것은 아니다.

Designed by Tistory.