§01Contributions
UDP 기반 전력 모듈 통신 프로토콜 구현
  • 실제 장비와 동일한 UDP 바이너리 프로토콜을 구현하여 전력 모듈과 통신
  • Polling 방식으로 전류·전압 등 측정값을 주기적으로 전송 및, 원격 스위치 제어 기능 구현
  • 이중화등 실제 장비와 동일한 통신 환경을 구성하여 기능 검증 지원
시뮬레이터 테스트 어플리케이션 화면 개발
  • Thymeleaf 기반 UI를 구현하여 시뮬레이터를 제어하는 관제 화면 개발
  • 사용자 입력을 규격 프로토콜로 변환하여 장비에 전송하고 응답 결과를 웹소켓을 사용하여 실시간 표시
멀티스레드 기반 장비 동시 구동
  • 하나의 프로그램에서 6종의 전력 모듈을 동시에 시뮬레이션하도록 멀티스레드 구조 설계
  • 장비별 독립적인 스레드를 운영하며 생명주기와 동시성 이슈 관리
  • 다수 장비를 동시에 구동하는 환경에서도 안정적인 시뮬레이션 지원
§02Problem Solving
PS-01실제 장비와 구분 불가능한 모듈 시뮬레이터 구현
문제 상황회사 어플리케이션의 통신 로직을 개발·검증하려면 통신 상대인 6종 전력 모듈이 필요했지만, 이들은 고가의 물리 장비라 6종을 모두 확보하기 어려웠음. 더 큰 문제는, 설령 장비를 갖추더라도 오류 응답·이중화 절체 같은 비정상 상황은 실장비로 재현하기 어렵고 위험해 안정적으로 테스트할 수단이 없었음. 그래서 실장비를 대신할 가상의 모듈 시뮬레이터를 직접 구현하기로 결정.
분석
  • 단순 목(mock) 수준으로는 회사 어플리케이션 서버의 실제 파싱·예외 처리 경로를 검증할 수 없다고 판단. 실장비가 고정된 UDP 바이너리 프로토콜로 통신하므로, 프레임을 바이트 단위까지 동일하게 재현하면 서버가 시뮬레이터와 실장비를 구분할 수 없음 → 초기 구현 비용이 크더라도 프로토콜을 완전 모사하는 방향을 선택

  • 6종 모듈은 하는일(계측값 폴링, 모듈 상태 정보 반환등) 동일하되 다루는 데이터·제어 동작만 상이. 타입별로 6개를 각각 구현하면 당장은 빠르지만 공통 로직이 중복되고 프로토콜 변경 시 6곳을 수정해야 함 → 공통부를 추상화하는 방향으로 결정.

  • 실장비는 main/backup 네이트워크 이중화 구조로 단일 회선만 모사하면 자원은 절약되나 서버의 이중화 절체 로직을 검증할 수 없음 → 시뮬레이터도 이중화 구조로 재현.
구현

1. Command Type, NACK Code, 모듈 타입 등 모든 바이너리 코드값을 Enum으로 관리. 코드량은 늘지만, 코드↔의미 매핑을 컴파일 타임에 고정해 타입 안정성과 가독성을 확보.

2. 바이너리 프레임과 자바 객체 간 직렬화/역직렬화를 전담하는 전용 클래스로 변환 책임을 분리(관심사 분리). 프로토콜 규격 변경의 영향 범위를 이 계층으로 국한.

3. 데이터의 생명주기에 따라 저장소를 분리. 계측값,포인트 상태 등 런타임 데이터는 재기동 후 무의미하므로 인메모리 휘발성으로 유지해 불필요한 쓰기 부하와 복잡도를 제거하고, 모듈 식별 정보(모듈 번호·타입)만 DB에 영속화. 서버 실행 시 이 정보를 읽어 6개 모듈 인스턴스를 구동

4. 실장비가 신뢰성 대신 낮은 오버헤드를 택해 UDP를 사용하는 만큼 중복 수신이 발생할 수 있음. 동일 요청의 수신 여부를 FCS(Frame Check Sequence)로 관리해 중복 응답·중복 제어 실행을 방지 — 제어 명령의 중복 실행이 의도치 않은 차단기 동작으로 이어질 수 있어 필수적인 처리.

5. AbstractModule <P extends AbstractPoint> 로 모듈과 포인트(모듈의 데이터 단위) 구조를 추상화. 공통 로직은 AbstractModule에 단일 구현하고 타입별 상이한 로직만 상속 클래스에 격리하며, 제네릭 상한 경계로 각 모듈이 자기 포인트 타입을 캐스팅 없이 타입 안전하게 다루도록 설계. 초기 설계 복잡도를 감수하는 대신, 신규 모듈 타입 추가 시 포인트·모듈 클래스만 추가하면 되는 확장 구조를 확보.

6. 모듈마다 UDP 소켓을 main/backup 2개씩, 각각 별도 IP·별도 쓰레드로 구동해 이중화를 재현하고, main 회선 여부를 플래그로 관리해 전송 실패 시 회선을 절체.

결과
  • 물리 장비 없이 회사 어플리케이션의 통신 로직을 개발·검증할 수 있는 환경 확보.

  • 정상 시나리오뿐 아니라 오류 응답·이중화 절체 등 엣지 케이스를 반복 재현할 수 있는 테스트 수단 마련.

  • 공통 로직 단일화와 모듈 추상화로, 프로토콜 변경은 공통부 한 곳만, 신규 모듈 타입은 클래스 추가만으로 대응 가능한 확장 구조 확보.
PS-02멀티 스레드 환경 동시성 제어
문제 상황이 시뮬레이터는 여러 개의 가상 장비를 동시에 구동하는데, 각 장비를 성격이 다른 여러 스레드가 동시에 다룬다 — (1) UDP 패킷을 받는 수신 스레드(회선 이중화를 위해 장비당 2개, 총 12개), (2) 주기적으로 장비 상태를 조회·전송하는 타이머 스레드, (3) API로 장비를 켜고 끄는 REST API 스레드. 즉 하나의 데이터를 여러 스레드가 동시에 읽고 쓰는 구조인데, 초기 구현에는 동기화가 없어 겉보기엔 멀쩡히 돌지만 특정 타이밍에만 값이 어긋나거나 종료가 안 되는, 재현하기 어려운 결함이 잠재해 있었다.
분석 과도하게 사용하는 락 없이 문제에 성격에 맞는 락을 사용하기 위해 공유되는 데이터를 접근 방식에 따라 3종류로 분류했다.
  • 단순 on/off 플래그 - 한 스레드가 바꾼 값을 다른 스레드가 즉시 봐야함 → 가시성 필요
  • read and write - 읽고 고쳐쓰는 도중 여러 스레드가 동시에 갱신하면 서로의 갱신을 덮어씀 → 원자성 필요
  • 다수가 공유하는 중앙 저장소 - 읽기가 압도적으로 많고 가끔만 변경됨 → 읽기 성능을 죽이지 않는 안전성
구현

1. volatile 적용으로 가시성 확보 : 각 장비의 수신 스레드가 반복 확인하는 종료 플래그를, 멀티코어 환경에선 다른 스레드가 변경 값이 CPU 캐시에 갇혀 전파되지 않을 수 있다(→ 멈추지 않는 유령 스레드). 이 플래그를 volatile로 선언해 변경이 즉시 모든 스레드에 보이도록 보장했다.

2. fcs(요청-응답 식별 번호) 관리 로직에 synchronized 적용으로 원자성 확보 : fcs 생성은 "읽고-고쳐-쓰기"의 다단계라, 두 스레드(소켓스레드, 타이머 스레드)가 동시에 수행하면 순번이 중복돼 짝짓기가 어긋났다. 순번 생성 메서드에 synchronized를 걸어 한 스레드가 이 과정을 원자적으로 끝내도록 했다. fcs는 15 다음에 0으로 되돌아 가야하는 규칙이 있어서 AtomicInteger를 사용할 경우 커스텀 규칙을 넣어 줘야하기 때문에 가독성 면에서 이점이 있는 synchronized 로 관리했다.

3. ConcurrentHashMap사용으로 안전성 확보 : 구동중인 모든 가상장비의 인스턴스는 HashMap으로 메모리에서 관리 된다. 읽기가 압도적으로 많고 변경은 적기 때문에 저장소 전체 락을 거는 것 보다는 ConcurrentHashMap 자료 구조로 변환하여 개별 연산(한 번의 조회, 한 번의 저장)단위로만 안전을 보장했다. 또한 computeIfAbsent 사용으로 TOCTOU를 방지할 수 있으나 이 프로젝트 상에서는 인스턴스 생성과 동시에 소켓 bind와 스레드 기동과 같은 동작을 하므로 만약 예외라도 생기면 락을 오래 쥐고 있을 수도 있어서 사용하지 않았다.

결과문제에 맞는 최소한의 조치로 락을 보장하여 성능은 크게 떨어지지 않으면서 안정성을 확보했다.