들어가며
지난 글에서는 EPSON 써멀 프린터 라이브러리의 전체적인 설계와 PrinterDialect, Adapter 패턴의 활용에 대해 살펴보았습니다.
이번 글에서는 각 연결 방식별 구체적인 구현을 자세히 분석해보겠습니다.
각 연결 방식별 어댑터는 다음과 같은 공통점과 차이점을 가지고 있습니다:
- 공통점:
T83PrinterAdaptor
추상 클래스를 구현하여 기본적인 프린터 명령어 전송 기능을 제공 - 차이점: 실제 프린터와의 연결 방식과 통신 방법이 다름
1. NetworkPrinterAdaptor 구현 분석
NetworkPrinterAdaptor는 TCP/IP 소켓을 통해 프린터와 통신하는 구현체입니다.
핵심 특징
internal object NetworkPrinterAdaptor : T83PrinterAdaptor() {
internal var tcpIp: String = ""
internal var port: Int = 9100
internal var timeout: Int = 1000
@Volatile
private var socket: Socket? = null
@Volatile
override var outputStream: OutputStream? = null
}
- Singleton 패턴: object 키워드를 사용하여 싱글톤으로 구현
- 상태 관리: socket과 outputStream을 @Volatile로 관리하여 스레드 안전성 확보(Volatile 어노테이션에 대한 자세한 내용은 아래 관련 포스팅 링크 참조)
- 설정 유연성: IP, 포트, 타임아웃을 설정 가능
2024.12.15 - [Language/Java] - [@Volatile] CPU 캐시와 메인 메모리 사이, @Volatile의 동작 원리 심층 탐구
[[@Volatile] CPU 캐시와 메인 메모리 사이, @Volatile의 동작 원리 심층 탐구
들어가며멀티코어 프로세서가 당연해진 현대의 애플리케이션 개발에서, 동시성 제어는 필수적인 고려사항이 되었습니다.그 중에서도 @Volatile 키워드는 자바의 동시성 제어에서 가장 기본적인
shin-e-dog.tistory.com](https://shin-e-dog.tistory.com/126)
연결 관리
override fun connect() {
disconnect()
try {
if (socket?.isConnected == true) return
val newSocket = Socket()
val address = InetSocketAddress(tcpIp, port)
newSocket.connect(address, timeout)
socket = newSocket
outputStream = socket!!.getOutputStream()
} catch (e: Exception) {
logger.error("connect error", e)
disconnect()
throw e
}
}
override fun disconnect() {
try {
socket?.close()
outputStream?.close()
} catch (e: Exception) {
logger.error("disconnect error", e)
} finally {
outputStream = null
socket = null
}
}
- 연결 전 기존 연결을 항상 해제
- 예외 발생 시 적절한 로깅과 리소스 정리
- finally 블록을 통한 안전한 리소스 해제
2. SerialPrinterAdaptor 구현 분석
SerialPrinterAdaptor는 RS-232 시리얼 포트를 통해 프린터와 통신하는 구현체입니다.
핵심 특징
object SerialPrinterAdaptor : T83PrinterAdaptor() {
internal var baudRate: Int = 38400
internal var dataBits = 8
internal var stopBits = SerialPort.ONE_STOP_BIT
internal var parityBits = SerialPort.NO_PARITY
internal var port: String = "COM2"
override var outputStream: OutputStream? = null
private var commPort: SerialPort? = null
}
- 시리얼 통신 설정: baudRate, dataBits 등 시리얼 통신에 필요한 설정 제공
- jSerialComm 라이브러리 활용: SerialPort 클래스를 통한 시리얼 통신 구현
연결 관리
override fun connect() {
disconnect()
commPort = SerialPort.getCommPort(port)
commPort?.openPort()
}
override fun outputStream(): OutputStream {
if (outputStream == null) {
commPort?.baudRate = baudRate
commPort?.numDataBits = dataBits
commPort?.numStopBits = stopBits
commPort?.parity = parityBits
outputStream = commPort?.outputStream
}
return outputStream ?: throw IOException("outputStream을 가져올 수 없습니다.")
}
- 연결 시점과 OutputStream 생성 시점 분리
- 필요한 시점에 시리얼 포트 설정 적용
3. Android SDK 어댑터 구현 분석
안드로이드용 구현은 두 가지 어댑터를 제공합니다:
- AndroidSerialPrinterApiAdapter: EPSON SDK의 high-level API 활용
- AndroidSerialPrinterByteArrayAdapter: raw byte array 전송 방식 활용
AndroidSerialPrinterApiAdapter
class AndroidSerialPrinterApiAdapter(
private val printer: Printer,
) {
internal var connectionType: AndroidSerialConnectionType = AndroidSerialConnectionType.USB
internal var address: String = ""
internal var deviceId: String = DEFAULT_DEVICE_ID
internal var timeout: Int = Printer.PARAM_DEFAULT
private var target: String = when (connectionType) {
AndroidSerialConnectionType.USB -> "$connectionType:$address"
AndroidSerialConnectionType.BLUETOOTH -> "$connectionType:$address"
AndroidSerialConnectionType.TCP -> "$connectionType:$address[$deviceId]"
AndroidSerialConnectionType.TCP_SSL -> "$connectionType:$address[$deviceId]"
}
}
- 다양한 연결 방식: USB, Bluetooth, TCP, TCP_SSL 지원
- EPSON SDK 활용: Printer 객체를 통한 고수준 API 사용
- 연결 문자열 동적 생성: 연결 방식에 따른 target 문자열 구성
AndroidSerialPrinterByteArrayAdapter
class AndroidSerialPrinterByteArrayAdapter(
private val printer: Printer,
) : T83PrinterAdaptor() {
override fun output(data: ByteArray) = ePos2ExceptionWrapper {
printer.addCommand(data)
}
fun sendData() {
printer.sendData(timeout)
}
fun clearBuffer() {
printer.clearCommandBuffer()
}
}
- 저수준 제어: raw byte array를 직접 전송
- 버퍼 관리: 명령어 버퍼 관리 기능 제공
- 예외 처리: ePos2ExceptionWrapper를 통한 통합적인 예외 처리
연결 방식별 특징과 차이점
1. NetworkPrinterAdaptor
- 장점
- 네트워크를 통한 원격 프린팅 가능
- TCP/IP 프로토콜의 안정성 활용
- 단점
- 네트워크 지연 가능성
- 네트워크 보안 고려 필요
2. SerialPrinterAdaptor
- 장점
- 안정적인 직접 연결
- 낮은 지연시간
- 단점
- 물리적 연결 필요
- 거리 제약 존재
3. Android SDK 어댑터
- 장점
- 다양한 연결 방식 지원
- EPSON SDK의 안정성 활용
- 단점
- 안드로이드 플랫폼 의존성
- SDK 버전 제약
마치며
각 연결 방식별 어댑터는 그들만의 고유한 특징과 장단점을 가지고 있습니다. 이러한 다양한 구현체를 통해:
- 사용 환경에 따른 최적의 연결 방식 선택 가능
- 동일한 인터페이스로 다양한 연결 방식 활용
- 새로운 연결 방식 추가가 용이한 확장성
각 연결 방식별 구현은 해당 통신 방식의 특성을 반영하면서도, 공통된 인터페이스를 통해 일관된 사용성을 제공합니다.
이는 앞서 설계한 추상화의 효과를 잘 보여주는 예시입니다.
다음 글에서는 각각의 출력 기능(텍스트, 바코드, QR 코드, 이미지)의 구현에 대해 자세히 살펴보도록 하겠습니다.