본문 바로가기
팀 프로젝트/cheerha.project

채용공고 데이터를 크롤링해오자(3) 코틀린과 함수형 프로그래밍 리팩토링

by pon9 2025. 3. 4.

함수형 프로그래밍이란?

프로그래밍 패러다임은 크게 두 가지로 나눌 수 있다.

명령형 프로그래밍(Imperative Programming), 선언형 프로그래밍(Declarative Programming)

명령형 프로그래밍은 "어떻게" 처리할 것인지를 순서대로 기술하는 방식이며, C, Java같은 전통적인 언어들이 여기에 속한다.

 

반면 선언형 프로그래밍은 "무엇"을 수행할 것인지를 강조하며, 대표적인 방식이 함수형 프로그래밍(Functional Programming, FP)이다.

클린코드 저자 로버트마틴은 함수형 프로그래밍을 "대입문이 없는 프로그래밍" 이라고 정의했다.

함수형 프로그래밍의 주요 개념으로는,

 

1. 순수 함수(Pure Functions)

함수가 외부 상태에 전혀 영향을 주지 않고, 같은 값을 넣으면 항상 똑같은 결과만 출력한다.

코드가 예측 가능해지고 문제가 생겼을 때 디버깅이 쉬워진다

 

2. 불변성(Immutability)

한 번 생성된 데이터는 변경되지 않는다.

 

3. 일급 및 고차함수(First-Class & Higher-Order Functions)

함수를 진짜 값처럼 다룰 수 있다. 함수를 변수에 넣거나, 다른 함수에 넘겨주거나, 함수에서 함수를 반환할 수 있다.

 

4. 부작용 최소화(Minimized Side Effects)

함수가 외부 상태를 변경하지 않도록 한다.

 

5. 함수 합성(Function Composition)

작은 함수들을 조합해서 더 큰 기능을 만든다.

코드가 깔끔해지고, 작은 조각으로 나눠서 재사용하기도 쉽다.

 

코틀린은 Java에서 파생되어, 객체지향 프로그래밍(OOP)와 함수형 프로그래밍을 동시에 지원하는 멀티 패러다임 언어다.

따라서 함수형 스타일을 활용해 코드를 더 읽기 쉽고 유지보수하기 좋은 방식으로 리팩토링 할 수 있다!

 

 

코틀린에서 제공되는 클래스

자바를 주 언어로 사용하다 이번에 코틀린을 처음 사용해보면서, 새롭게 제공되는 클래스 두 개를 찾을 수 있었다.

data class와 object인데, 사용해보니 static이랑 비슷한 듯 하면서도 왜 둘은 분리되어있을까? 하는 궁금증이 생겼다.

 

1. data class - 데이터를 담는 데 최적화된 클래스

자동으로 toString(), equals(), hashCode(), copy()메서드를 생성해준다.

보통 DTO, VO같은 불변 객체를 만들 때 사용한다.

(아 생각해보니 record가 얘구나)

 

2. object - 싱글톤 패턴을 쉽게 구현

코틀린에서 object키워드를 쓰면, 클래스를 하나만 생성하고 전역적으로 공유할 수 있다.

자바의 static과 비슷한 역할을 할 수 있지만 클래스 자체가 싱글톤으로 동작한다.

 

코틀린에서 클래스 내부에 companion object를 사용하면 자바의 static과 비슷하게 인스턴스를 만들지 않고도 정적 메서드처럼 사용할 수 있다.

 

 

현재 코드의 문제점

놀랍게도 여기서 끝이 아니다..

 

1. 관심사 분리가 부족하다(SRP)

크롤링 흐름, 필터 설정, 데이터 파싱이 한 클래스에 모두 포함되어있다.

crawl()함수가 너무 커서 유지보수하기 힘들다.

>> 크롤러, 필터링, 데이터 파싱을 각각의 클래스로 분리하자!

 

2. 반복되는 코드가 많다

필터 클릭, 페이지 이동, Thread.sleep()같은 반복적인 동작이 많다.

불필요한 중복을 제거해야 가독성과 유지보수성이 향상된다

>> 코틀린의 apply, also 등을 사용해 중복되는 로직을 제거하자!

 

3. 명령형 프로그래밍 스타일이 강하다

for루프를 사용한 직접적인 반복과, 개별적으로 선언된 변수 등 명령형 스타일이 많다.

>> 함수형 프로그래밍에서 제공되는 메서드를 사용해보자!

 

 

1. 필터 로직 분리

우선 필터를 object로 분리해서, 해당 로직을 한 곳에 모아 관리하기 쉬워졌다

크롤러 코드에서 JobKoreaFilters.apply(wait) 로 편하게 호출 가능하며, 잡코리아의 필터 사용 정책이 바뀌면 이 부분만 수정하면 된다!

val mainJobElement = wait.until(
    ExpectedConditions.presenceOfElementLocated(By.cssSelector("label[for='duty_step1_10031']"))
)
wait.until(ExpectedConditions.elementToBeClickable(mainJobElement)).click()
thread.sleep(3000) //UI 대기 (3초)

그리고 기존엔 java의 명령형 프로그래밍에 익숙해져서 코틀린의 이점을 제대로 사용하고 있지 못했는데,

함수형 프로그래밍 스타일로 리팩토링 해서 변수 사용을 최소화해보았당

 

 

2. 데이터 파싱 로직 분리

코틀린에서 제공하는 data클래스로 분리해서, 크롤링 대상의 html 구조가 바뀌면 이부분만 수정하면 되게끔 분리했다

중간 변수를 최소화해서 한 줄로 바로 값을 계산하도록 리팩토링했다. 각 필드의 값을 바로 계산해서, 불필요한 변수 선언을 줄였다.

또한 hiringStartAtText, hiringStartAt처럼 두 변수로 분리해서 사용하던 로직을 함수형 프로그래밍의 이점을 살려 변수 하나로 처리해 코드의 가독성을 높일 수 있었다.

이처럼 크롤러에서는 간편하게 JobKoreaContentData.from만 호출하면 된다!