들어가며
입사 2개월 차였던 2023년 11월, 저는 Epson 프린터를 제어하기 위한 라이브러리를 개발했습니다.
당시에는 "돌아가는 코드"를 만드는 것에 집중했고, 실제로 지금까지 다행히도 프로덕션 환경에서 안정적으로 동작하고 있습니다.
하지만 1년이라는 시간이 지난 지금, 조금은 더 넓어진 시야로 이 코드를 돌아보니 몇 가지 개선점이 보입니다.
주요 문제점과 개선 방향
1. 단일 책임 원칙 위반
가장 큰 문제점은 PrinterDialect
객체에 너무 많은 책임이 집중되어 있다는 점입니다.
PrinterDialect 오브젝트의 역할은 아래와 같아요.
- 기본 명령어 상수 관리
- 텍스트 관련 명령어 생성
- 바코드 관련 명령어 생성
- QR 코드 관련 명령어 생성
- 이미지 관련 명령어 생성
이와 같이 다양한 책임을 가지고 있습니다. 이를 각각의 전용 Factory 객체로 분리하여 단일 책임 원칙을 준수하도록 개선해야 합니다.
2. 에러 처리의 일관성 부족
현재 코드의 에러 처리는 다소 혼란스럽습니다:
// 어떤 메서드는 Result를 반환
fun validationCheck(): Result<Unit>
// 어떤 메서드는 예외를 throw
fun connect() {
if (!isValid) throw IllegalStateException()
}
이는 제가 외부 세계, 특히 하드웨어 의 명령 결과는 Result로 표현하는게 좋겠다라는 나름의 개발 컨벤션이 생기기 전이라 개선할 필요가 있습니다.
3. 설정값 관리의 분산
현재는 설정값들이 여러 곳에 흩어져 있고, 검증도 부족합니다:
const val DEFAULT_DEVICE_ID = "local_printer" // 여기저기 흩어진 상수들
internal var timeout: Int = Printer.PARAM_DEFAULT // 검증 없는 설정값
이를 중앙화된 설정 관리와 검증 로직으로 개선해야 합니다:
data class PrinterConfig(
val connection: ConnectionConfig,
val printer: PrinterSettings,
val timeout: TimeoutSettings
) {
fun validate() {
connection.validate()
printer.validate()
timeout.validate()
}
}
4. 타입 안전성 부족
현재는 ByteArray를 직접 다루는 부분이 많아 타입 안전성이 부족합니다:
fun output(data: ByteArray) // 어떤 명령어인지 타입으로 알 수 없음
이를 value class를 활용하여 다음과 같이 개선할 수 있습니다:
@JvmInline
value class PrinterCommand(val bytes: ByteArray)
fun output(command: PrinterCommand) // 타입 안전성 확보
배운 점: 추상화의 힘을 실감하다
이 라이브러리를 통해 많은 것을 배웠지만, 가장 큰 교훈은 '올바른 추상화의 가치'였습니다.
처음 이 라이브러리를 만들 때는 네트워크 연결만 구현하면 되는 상황이었습니다.
하지만 저는 "프린터와의 통신은 결국 동일한 명령어를 전달하는 것이고, 전달 방식만 다르다"는 점에 주목했습니다.
이에 따라 각 연결 방식의 공통되는 부분을 추상화 하여 추상 클래스를 만들고, 각 연결 방식을 구현체로 구현하는 설계를 선택했죠.
당시에는 이것이 얼마나 중요한 결정이었는지 몰랐습니다.
하지만 최근 안드로이드 POS용 프린터 개발을 하면서 그 가치를 실감하였습니다.
안드로이드 SDK를 사용한 USB 연결 지원이 필요해졌을 때, 기존 구조 덕분에 어댑터 코드만 새로 작성하면 되었기 때문입니다.
만약 처음부터 이런 추상화 없이 네트워크 연결에 특화된 코드를 작성했다면, 안드로이드 지원을 위해 전체 구조를 뜯어고치거나 새로 개발해야 했을 것입니다.
추상화 덕분에 개발 시간이 대폭 단축되었고, 코드의 재사용성도 높일 수 있었습니다.
이는 제게 추상화의 힘을 몸소 체험하게 해준 귀중한 경험이었습니다.
비록 코드 곳곳에 개선의 여지가 있지만, 이 핵심적인 설계 결정은 지금 봐도 제법 잘한 선택이었다고 생각합니다.
마치며
지금도 아직 1년차 햇병아리 개발자이지만, 그러한 지금보다도 더 미숙했던 2개월 차 시절의 코드를 돌아보면 많은 개선점이 보입니다.
PrinterDialect의 단일 책임 원칙 위반, 에러 처리의 일관성 부족, 설정값 관리의 분산 등 아쉬운 점이 많았죠.
하지만 동시에 이 라이브러리는 제게 추상화의 중요성을 가르쳐준 첫 실전 경험이었고, 앞으로의 설계에도 이 교훈을 잘 활용하고 싶습니다.
(개발자로서 지난 1년을 회고하며, 제작했던 라이브러리에 관한 포스팅을 하고자 했을때, 제가 개발했단 사실 조차 잊고 있던 라이브러리 이긴 하지만요 ㅋㅋ;; 너무 오래됨...)
무튼 저는 앞으로도 계속해서 더 나은 코드를 작성하기 위해 노력하겠습니다.
특히 이 떄의 경험을 통해 배운 "올바른 추상화의 가치"를 항상 기억하며, 확장성과 유지보수성을 고려한 설계를 해나갈 생각입니다.
긴 시리즈 글 읽어주셔서 감사합니다.