1. 세션 요약 - 빈칸 채우기
(기본 원칙) 개발할 때 _______, _______, _______을 구분해서, _______과 _______의 범위는 늘리고 _______의 범위는 줄여야 한다.
(데이터 / 계산 / 액션 전문가 6단계)
1. 요구사항에 따라 작업할 부분에 대한 전체적인 흐름을 파악한다.
2. 최종적으로 해야 하는 _______을 정의하고, 그 액션에 필요한 _______ 형태를 확인한다.
3. 데이터가 들어오는 _______ 이후부터, _______ 직전까지의 구간을 _______ 구간으로 정의한다.
4. '계산' 구간을 몇 개의 _______로 나누어 처리할지 정의하고, 각 계산에 필요한 _______을 알아낸다.
5. 코드가 최초 _______과 _______ 되도록 번갈아 읽으며 점검하고, _______ 한다.
6. 모든 작업을 완료한 뒤 정상 동작을 확인한다.
2. 세션 목표
개발을 하다보면 다음과 같은 어려움을 빈번히 겪게 됩니다
•
함수 안에 너무 많은 것들이 엮여 있어서 읽거나 건드리기 힘들다
•
거대한 함수에 대해 변경을 가했을 때 결과가 잘 예측되지 않아 불안하다
•
복잡하게 얽혀 있는 코드를 어떤 기준으로 분리하거나 구분해야 할지 모르겠다
이번 세션에서 다루는 내용을 잘 이해하면,
•
코드의 순수하고 신뢰할 수 있는 영역(데이터, 계산)과, 불확실한 영역(액션)의 경계선을 그어 바라볼 수 있게 됩니다.
•
신뢰할 수 있는 영역인 데이터, 계산의 범위는 늘리고, 불확실한 영역인 액션의 영역은 축소하는 방법을 익히게 됩니다.
3. 인접 개념
•
관심사의 분리 (SRP, Single Responsibility Principle)
•
순수 함수 (Pure Function) / 부수 효과 (Side Effect)
•
멱등성 (Idempotency)
•
의존성 역전 (IoC, Inversion of Control)
•
4. 멘탈 모델 & 시각화 자료
// 데이터: 외부에서 온 것들(네트워크, DOM, URL, 스토리지)
raw = readData()
// 계산(순수): raw → 결과. 같은 입력이면 항상 같은 출력.
result = compute(raw)
// 액션: 화면/네트워크/스토리지/라우터 같은 경계와 상호작용.
doEffect(result)
TypeScript
복사
before: 데이터 / 계산 / 액션이 하나의 함수에서 뒤섞여 있음
after: 액션과 데이터 / 계산이 분리되어 있음
5. ‘데이터 / 계산 / 액션’ 패턴이란?
•
신뢰할 수 있는 '데이터 / 계산'의 영역을 최대한 넓히고, 예측 불가능한 '액션'의 영역을 최대한 작고 고립된 경계 밖으로 몰아내는 것.
•
‘액션 → 계산 → 데이터’ 순으로,
◦
외부 요소에 대한 ‘얽힘(Entanglement)’ 정도가 줄어든다. 이는 외부 요소에 대한 의존으로도 바꾸어 말할 수 있다.
◦
고려해야 하는 외부 요소가 줄어들기 때문에 인지 부하가 낮아지며, 단순하여 신뢰할 수 있는 코드가 된다.
•
데이터 (Data)
◦
변하지 않는 사실 그 자체입니다. 숫자, 문자열, 일반 객체 등 값과 정보를 나타냅니다.
◦
데이터는 거짓말을 하지 않으므로 100% 신뢰할 수 있습니다.
•
계산 (Calculation)
◦
데이터를 입력받아 새로운 데이터를 출력하는 '순수한 공장'입니다. 같은 재료를 넣으면 언제나 같은 제품이 나옵니다.
◦
외부 세계에 어떤 영향도 주지 않으므로(부수효과 없음), 계산 역시 100% 신뢰할 수 있습니다. (a, b) => a + b 와 같은 순수 함수가 여기에 해당합니다.
•
액션 (Action)
◦
외부 세계와 소통하며 '변화'를 일으키는 모든 것입니다.
◦
API를 호출하거나, 데이터베이스에 저장하거나, console.log를 찍거나, 심지어 new Date()나 Math.random()처럼 실행할 때마다 결과가 달라지는 코드도 모두 액션입니다.
◦
액션은 외부 환경에 의존하기에, 본질적으로 예측 불가능하며 우리가 의심하고 신중하게 다뤄야 할 대상입니다.
6. 예제 풀이
const name = "김토스" // ?
console.log(name) // ?
const upper = name.toUpperCase() // ?
localStorage.getItem('user') // ?
const sum = a + b // ?
new Date() // ?
[1,2,3] // ?
Math.random() // ?
TypeScript
복사
7. 전문가들의 ‘데이터 / 계산 / 액션’ 패턴
7-1. 미니 과제 - Before
7-2. 전문가 영상
•
데이터 → UI 변환용 aggregate 레이어 추가 (0:50~6:08)
•
날짜 정렬, 그룹핑 로직 구현 (0:00~2:00, 7:30~8:50)
•
Suspense, 쿼리 옵션, Hook 분해 후 재설계 (3:00~8:30)
•
…
전문가들은 ‘데이터 / 계산 / 액션’을 나눌 때 아래와 같은 단계를 거친다.
1.
요구사항에 따라 작업할 부분에 대한 전체적인 흐름을 파악한다.
•
ex1. “서버에서 데이터를 컴포넌트에 가지고 온 다음 그룹핑을 해서 화면에 뿌려야 해.”
•
ex2. “Form을 Submit 하는 함수가 복잡해서 확실한 영역과 불확실한 영역을 분리해야겠어.”
2.
최종적으로 해야 하는 액션을 정의하고, 그 액션에 필요한 데이터 형태를 확인한다.
•
ex1. “결국 컴포넌트로 렌더링할 때 필요한 데이터 형태는 B구나.”
•
ex2. “결국 form.submit() 함수에 넘겨줄 데이터 형태는 B구나.”
3.
데이터가 들어오는 input 이후부터, 액션 직전까지의 구간을 ‘계산’ 구간으로 정의한다.
•
ex1. “서버에서 가져온 데이터 형태는 A이고, 컴포넌트로 렌더링할 때는 B라는 형태로 넘겨줘야 해.“
•
ex2. “form으로 받은 input의 데이터 형태는 A이고, submit 함수에 B라는 형태로 넘겨줘야 해.”
4.
‘계산’ 구간을 몇 개의 덩어리로 나누어 처리할지 정의하고, 각 계산에 필요한 입출력을 알아낸다. 즉, 각 계산이 어떤 input / output으로 맞물리게 할지 결정한다.
•
ex1. “서버에서 가져온 A를 B로 만드는 동안, 1-1) 정렬 1-2) 그룹핑 1-3) UI 형태에 맞게 포매팅이 되어야 해. UI 형태에 포매팅 하는 부분은 다시 2-1) 금액 쉼표 처리 2-2) 타입별 색상 매핑 등의 작업이 있지만 이 부분은 이쪽 계산에서 처리하지 않고 컴포넌트에 인라인 할래.“
•
ex2. “form으로 받은 데이터 A를 B로 만드는 동안, 1) 각 필드에 대한 유효성 검사를 한 뒤 2) 특정 필드의 포매팅해야 해. 유효성 검사는 zod schema에서 예외를 던지게 해 catch에서 잡아서 처리하고, 포매팅의 경우 계산 함수에서 책임지도록 할거야.”
•
주의 사항
◦
‘데이터 → 계산 1 / 2 / 3 / … → 액션’ 형태로 맞물리게 한다.
◦
계산 함수를 작성할 때, 반드시 함수의 이름과 인터페이스로부터 동작과 리턴 값이 예측 가능하도록 한다. 이를 위해 함수의 인터페이스를 세부 구현보다 먼저 작성한다.
◦
한번에 여러가지 역할과 책임을 처리하는 몬스터 함수를 피한다. 이 경우 더 작은 함수로 쪼개어 다시 input / output이 맞물리도록 한다.
5.
코드가 최초 요구사항과 1:1 매칭 되도록 번갈아 읽으며 점검하고, 재조정 한다.
•
함수의 이름, 인터페이스, 추상화 단위가 적절한지 점검한다
◦
요구사항과 코드가 매칭되어 위에서 아래로 글처럼 읽히는지
◦
함수의 이름과 인터페이스만으로 동작을 예측할 수 없어 시점 이동이 일어나지 않는지
▪
formatDate(date) → formatDate(date, "YYYY-MM-DD")
•
함수의 책임이 아닌 것을 외부로 위임하기 위해 IoC(ex. callback을 인자로 받기)를 고려한다 https://evan-moon.github.io/2023/01/15/what-is-abstract
6.
모든 작업을 완료한 뒤 정상 동작을 확인한다.
7-3. 미니 과제 - After
8. Wrap-up
(기본 원칙) 개발할 때 데이터, 계산, 액션을 구분해서, 데이터와 계산의 범위는 늘리고 액션의 범위는 줄여야 한다.
(데이터 / 계산 / 액션 전문가 6단계)
1. 요구사항에 따라 작업할 부분에 대한 전체적인 흐름을 파악한다.
2. 최종적으로 해야 하는 액션을 정의하고, 그 액션에 필요한 데이터 형태를 확인한다.
3. 데이터가 들어오는 입력 이후부터, 액션 직전까지의 구간을 계산 구간으로 정의한다.
4. '계산' 구간을 몇 개의 덩어리로 나누어 처리할지 정의하고, 각 계산에 필요한 입출력을 알아낸다.
5. 코드가 최초 요구사항과 1:1 매칭 되도록 번갈아 읽으며 점검하고, 재조정 한다.
6. 모든 작업을 완료한 뒤 정상 동작을 확인한다.


