최근 함수형 프로그래밍을 연습하던중 비동기 데이터를 받는 함수를 평소 사용하는 Future 대신 Task로 사용하는 것이 좀더 함수형 스타일에 가깝다는 것을 알게되었다. Future야 머.. 너무 많이 쓰이고 다른 언어에도 다 있는거라 당연하다고 치지만 Task..? 이건 Swift Concurrency 에서 보고 다른 언어 쓸 땐 못봤던 네이밍인데 이게 왜 Future 대신 함수형 프로그래밍에서 쓰이는거지..? 라는 궁금증이 생겼다. 그래서 코드를 작성해보고 로그를 확인해보니, Future와 Task의 차이에 대해 확실히 알게되었다.
위 코드를 통해 알 수 있는 차이점
실행 시점 | 함수 호출 시 즉시 실행. | run() 호출 시 실행. |
로그의 순서 | fetchNumber 호출 직후 "Fetching number..." 출력. | Task 생성 시 아무 작업도 실행되지 않음. |
지연 평가 | 지원하지 않음. | 실행 시점을 명시적으로 제어 가능. |
같은 로직을 왼쪽은 Future로, 오른쪽은 Task로 구현해보았다. 처음봤을 때는 이게 무슨 차이인데..? 그냥 .run() 이거 있고 없고 차이 아니야? 함수 실행이 바로되나 나중에 run으로 실행하는거나 무슨 차이지..? 라는 생각이 들 것이다.
위키피디아를 찾아보면 이게 바로 지연평가(lazy evaluation)라는 것이다~ 라며 좀만 스크롤 내리면 외계어같은 설명들이 등장한다. 이때까지만 해도 개념적으로는 그래, 나중에 평가되는거, 그래서 이게 무슨 의미가 있는데? 라는 의문이 남았던 것 같다.
이 코드부터 생각이 조금씩 달라졌다. 이번에도 같은 로직을 왼쪽은 Future, 오른쪽은 Task로 구현한 코드이다. 일단 눈에 띄는점은 오른쪽 코드에서 아직 실행도 시키지 않은 함수에 flatMap 함수로 로직전개를 하고있다는 것이다!!! (이거 저만 신기한거 아니죠?ㅋㅋ) 평소 모나드를 에러처리할 때 Result타입으로만 썻지 함수에 모나드가 적용되는 걸 직접보니 매우 흠터레스팅했다.
이부분을 좀더 생각해보면 아직 실행도 시키지 않은 함수를 flatMap 으로 다른 함수와 연결한다는 것은 일종의 로직을 합쳐서 다시 하나의 함수로 만들어 변수에 담아서 들고 다니는 거고 이게 바로 합성함수로구나를 느낄 수 있었다. 각 작업이 단일 책임을 가지면서도 사이드이펙트 없이 하나의 함수로 합성되는 것이다. 하지만 Future는 변수에 로직을 담는게 불가하다. 왜냐하면 하나의 Future함수는 일단 실행되면 값이라는 상태가 튀어나오기 때문이다.
Future의 가장 큰 문제점은 호출 시점에 실행되기 때문에, 호출과 실행이 분리되지 않는 다는 것이다. 위와 같이 Future는 한 번 실행된 결과를 재사용하여 동일한 객체를 await하면 같은 결과를 반환한다. 반면 Task는 매번 실행되는 구조로 실행 타이밍 제어 및 불변값 관리가 필요한 로직에서 빛을 보게된다.
이외에도 테스트 코드 모킹이 쉬워진다, 코드가 깔끔해진다 등등 장점이 많지만 내가 직접 체감한 것은 무엇보다 로직 전개가 너무 우아하고 깔끔해진다. Future를 다른 함수와 체이닝할 때 일단 튀어나오는 값을 어떻게 넣고 빼고 또 다음에 나오는 결과값을 어떻게 넣고 빼지..? 라는 다소 지저분한 고민을 해야한다면 Task는 결과값에 상관없이 순수 로직들을 어떻게 체이닝하지? 라는 깔끔한 고민을 할 수 있게 된다.
맺음말로 Task는 단순히 실행 시점을 지연시키는 도구가 아니다. Task를 통해 개발자는 코드의 의도와 실행을 명확히 분리 할 수 있다. 이는 마치 의존성 역전처럼 더이상 컴퓨터 실행순서에 끌려다니는게 아니라 개발자의 의도대로 코드 단위를 먼저 짜고 그 이후에 실행시키는 갓갓 도구인 것이다. 개발자가 가장 다루기 어려운 "상태"의 노출을 아무리 거대한 로직이라도 작은 순수함수들이 합쳐지면 입구와 출구 두곳만 잘 지켜보고 있으면 된다는게 특히 좋은 부분이다. Task를 잘 활용한다면 더나은 시스템 설계로 가는 것뿐만 아니라 프로그래밍 사고방식의 전환이 될 수 있겠다라는 생각이 들었다.
'Flutter' 카테고리의 다른 글
Expanded 과 Flexible 의 차이 (0) | 2025.01.25 |
---|---|
Segment 버튼에 애니메이션 적용하기 (0) | 2025.01.25 |
디자인시스템 버튼 비활성화를 구현하면서 마주친 트러블 슈팅 (0) | 2025.01.16 |
코디네이터 패턴을 플러터에 적용하며 생긴 _debugLocked 에러 (0) | 2025.01.14 |
iOS와 다르게 왜 Flutter에서는 Code Gen이 흔한걸까? (1) | 2025.01.04 |