코넥트 프로젝트를 진행하던 도중 나의 리액트 선생님이신 윤호님께 관심사의 분리와 선언적 프로그래밍에 대해서 듣게 되었다. 굉장히 흥미롭고 혁신적인 내용이었고, 앞으로 내가 코딩 함에 있어서 굉장히 중요한 요소로 작용할 거 같다는 느낌이 팍팍 들어 이번기회에 여러가지 자료를 찾아보고 공부해본 후 내용을 정리해볼까 한다.
관심사의 분리란?
간단한 공부를 위한 코드작성이나 규모가 작다면 모르겠지만, 우리는 코드를 작성하다보면 하나의 함수, 변수, 클래스, 컴포넌트에게 너무나 많은일을 한번에 부여하게 되는 경우가 생기게 된다. 그렇게 긴 코드를 작성하게 되면 읽는 사람은 물론 나 자신도 이게..뭐어..라고..했지...? 하는 상황이 발생하게 된다.
바로 이런 상황에서 필요한 것이 '관심사의 분리' 이다. 관심사의 분리는 말그대로 한번에 한가지 일만 하도록 단위를 잘게 나누어서 단위별로 하나의 관심사만 갖도록 하자! 라는 것을 의미한다.
이렇게 단위를 잘게 나누면 코드를 딱 봤을때 한눈에 파악하기 쉽기때문에 오류가 생겼을때나 수정이 필요할때 빠르게 해결할 수 있다는 장점이 있다. 이런것을 멋진말로 Divide & Conquer(분할 & 정복)이 용이하다 라고 한다.
관심사의 분리가 적절히 된 코드는 결합도가 낮고 응집도가 높다고 표현하곤 하는데,
여기서에 결합도는 '각각의 코드가 서로 얽혀있지 않다!' 를 의미하고
응집도는 '유사한 내용끼리 잘 묶여있다!' 를 의미한다.
그래서 우리는 결합도와 응집도 사이에 그 적절한 지점을 찾아 관심사의 분리를 적용해주어야 한다.
프론트엔드 개발에서의 관심사의 분리는 View(UI적인 부분) 와 Logic의 분리로 이루어진다.
아직 관심사의 분리도 덜 되고 초보여서 엉망징창인 로그인 페이지에서의 내 코드를 데리고 와 일단 설명을 해보자면...
먼저 View와 관련된 코드에서는 input 태그를 활용하여 입력받을 수 있는 창이 뜨게끔 만들어주었다. 위 사진을 보면 한 함수 안에서는 오직 UI와 관련 된 부분만 적혀져 있다는 것을 알 수 있다. 그러면 로직과 관련 된 부분은 어디에 있냐?
바로 여기에서 관리를 해주고 있다. 위 View와 관련된 코드에서 input창으로 받은 값들을 props로 넘겨 로직과 관련 된 부분은 모두 여기서 관리를 해주고 있는것이다. 이 페이지 전체를 보여주진 못했지만 UI 와 관련 된 부분은 이 함수에선 다루고 있지 않다.
위와 같이 분리 하면 한 함수, 컴포넌트, 클래스 당 코드가 간단해지고 오류 해결 수정등이 용이해진다.
내 코드가 아직 많이 부족하다고 말한 이유는 props 관리를 중앙집중적으로 해주고 있지 않아, 나중에 props들이 반복되고 많아져 코드가 길어지게 될 가능성이 높고, UI적인 부분에서 register 함수를 보면 이메일 입력 관련 규약 부분이 어떤 역할을 수행하고 있는지 한눈에 알아보기 어렵기도 하기 때문이다.
위와 같이 props와 관련 된 문제는 사실상 상태관리 라이브러리 (redux, recoil)를 사용해주면 가장 쉽게 해결할 수 있긴 한데, 우리는 프론트엔드 담당자이기 이전에 개발자 이기때문에 라이브러리를 최대한 사용하지 않고 context api를 활용하여 기본 단계부터 천천히 이해해보고 구현해보자고 이야기 하였다.
register 함수(react-hook-form에서의 기능)도 로직과 관련된 부분이 포함되어져있기 때문에 라이브러리를 사용하지 않고 직접 custom hook을 제작하여 관심사의 분리를 해나갈 예정이다.
같은 팀원분의 코드는 너무나 완벽하게 정리가 되어 있는 상태이지만,
아직 내 코드는.... 완벽하게 관심사의 분리가 안 되어 있다... 하하
너무너무 민망하지만... 설명을 위해...가지고 왔ㄷ..ㅏ
이렇게 View와 Logic으로 나눠줌으로써 한 컴포넌트에선 하나의 책임만 지게 되고 이는 재활용하기에도 쉽고 다른 컴포넌트들과 조합하기에도 쉬워졌다.
custom hook을 제작하는 방법 또한 관심사의 분리라고 말할 수 있는게, 로직을 독립적인 함수로 분리하여 여러곳에서 재활용 할 수 있는 형태로 만들어지기 때문이다.
이렇게 위와 같이 관심사의 분리를 해주고 props들은 중앙집중적으로 관리 해주면 좋은 형태의 코드를 만들 수 있을 것이다.
선언적 프로그래밍
더 좋은 코드를 만들자에서의 또 다른 키워드는 바로 명령형 코드 / 선언형 코드 이다.
먼저 명령형 코드가 무엇이고 선언형 코드가 무엇인지에 대하여 살펴보자.
명령형은 WHAT?을 의미하고 선언형은 HOW?를 의미한다.
잉? 이게 뭔소리여 싶은 사람들은 밑에 글을 조금 더 살펴보자.
(사실 뭐 내가 너무 간단하게 설명해서 여기서 바로 뭔 말인지 알아들으면 당신은... 천재..)
내가 너무너무 존경하는 진유림님 블로그 글에서의 코드를 잠깐 빌려오면,
function double(arr) {
let results = []
for (let i = 0; i < arr.length; i++) {
results.push(arr[i] * 2)
}
return results
}
arr 배열에 있는 모든 숫자를 하나씩 제곱하여 results 배열에 넣어주어라! 라는 명령형 코드.
function double(arr) {
return arr.map(item => item * 2)
}
모든 arr배열에서의 숫자가 제곱된다! 라는 선언형 코드.
위 두 코드를 살펴보면 HOW? WHAT?이 무슨 의미였는지 감이 잡힐 것이다.
명령형 코드에선 어떻게 코드가 동작되는지에 초점이 잡혀져 있고 선언형에서는 이 코드가 무엇인지에 조금 더 초점이 잡혀져 있다.
그렇다고 해서 선언형 코드에 WHAT만 담겨져 있냐? 그건 아니다.
선언형 코드는 명령형 코드에서 HOW부분이 숨겨져 있는 형태라고 생각하면 편할 것이다.
오~~그럼, WHAT?에 관한 부분만 노출하면 그것은 선언형 코드라고 할 수 있는건가~? 근데 그건 또 아니다.
온전히 WHAT에 관한 부분만 노출하게 되면, HOW에 관련된 부분이 명시되어 있지 않으니, 코드가 잘못 사용되거나 꼬여버릴 수 있기 때문이다. 그렇기 때문에 선언적 함수는 HOW에 관련된 부분이 적절하게 나타나져 있고, 언제 불러도 같은 결과를 줄 수 있는 함수! 여야 한다.
1. HOW에 관련된 부분이 적절하게 추상화 되어져 있고
2. 언제 불러도 같은 결과를 적절히 줄 수 있고
3. WHAT이 함수명에 적절히 표현 되었으면
그제서야 그것이 선언형 코드 라고 말 할 수 있는것이다.
위와 같이 선언적으로 코드를 작성하게 되면 읽기도 편하고 오류수정, 재사용에도 편하게 된다는 장점이 있다.
명령형으로 코드를 작성하는 것도 좋지만 아무래도 읽는 시간이 좀 더 걸리게 되니....
그래서 나도 앞으로 코드를 짤 때 선언적인 형태의 코드로 작성 해볼 예정이다.
프로젝트를 진행하며 초반부분과는 다르게 어떻게 구현할것인가 뿐만아니라 코드의 재사용성과 같은 리펙토랑 관련 부분들을 어떻게 향상 시킬 것이냐에 대해서 좀 더 많이 고민하게 되는 것 같다. 같이 프로젝트를 진행하며 좋은 정보들을 가지고 함께 이야기 나눠준 윤호님께 감사드리고 앞으로 나도 이번주에 공부하여 알게된 관심사의 분리와 선언형 코드를 활용하려 코드를 작성해야겠다고 생각했다.
https://velog.io/@eddy_song/separation-of-concerns
선언적 프로그래밍 참고 -
https://milooy.github.io/dev/220810-abstraction-and-declarative-programming/