개발은 아름다워

[ 실무 회고록 ] fcm으로 보낼 데이터 구조 작성 시 성능 개선 본문

회고록

[ 실무 회고록 ] fcm으로 보낼 데이터 구조 작성 시 성능 개선

do_it_zero 2025. 1. 24. 16:39

- 맡게 된 업무

기존의 HTTP 및 XMPP API는 2023년 6월 20일부로 지원이 중단되었습니다.

따라서, 기존에 쓰던 방식을 HTTP v1 API방식으로 바꿀 필요가 생기게 되었습니다.

 

- 기존 방식

HashMap을 생성하여 데이터를 받은 후 Json으로 만들어주는 라이브러리를 사용하여 fcm에 데이터를 전송하였습니다.

 

- 의문과 해결 방법 선택

단순히 fcm에 데이터를 보내기 위한 구조를 맞추기 위해서 HahsMap 객체를 여러개 생성하는게 맞을까 하는 의문이 들었습니다. 왜냐하면 고정된 문자열에 변수 값만 바뀌는 것이기 때문입니다. 이때 이펙티브 자바의 아이템6번 '불필요한 객체 생성을 피하라' 가 생각났습니다.

그래서 구조는 동일하게 작성되며 객체 생성 비용이 추가로 발생하지 않도록 StringBuilder를 사용하는게 나을 것이라고 판단하였습니다. 

 

- 해결 방법을 증명할 코드 작성

public class FcmPayloadComparison {
    private static final String DEVICE_TOKEN = "DEVICE_TOKEN";
    private static final String TITLE = "Hello";
    private static final String MESSAGE = "This is a test message";

    public static void main(String[] args) {
        int iterations = 1000;

        long startStringBuilder = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            createPayloadWithStringBuilder();
        }
        long endStringBuilder = System.nanoTime();

        long startHashMap = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            createPayloadWithHashMap();
        }
        long endHashMap = System.nanoTime();

        double timeStringBuilder = (endStringBuilder - startStringBuilder) / 1_000_000.0;
        double timeHashMap = (endHashMap - startHashMap) / 1_000_000.0;

        System.out.println("StringBuilder 구조인 경우의 걸린 시간: " + timeStringBuilder + " ms");
        System.out.println("HashMap 구조인 경우의 걸린 시간: " + timeHashMap + " ms");
    }

    private static String createPayloadWithStringBuilder() {
        StringBuilder payload = new StringBuilder();
        payload.append("{")
                .append("\"message\": {")
                .append("\"token\": \"").append(DEVICE_TOKEN).append("\",")
                .append("\"data\": {")
                .append("\"title\": \"").append(TITLE).append("\",")
                .append("\"body\": \"").append(MESSAGE).append("\"")
                .append("}")
                .append("}")
                .append("}");
        return payload.toString();
    }

    private static String createPayloadWithHashMap() {
        Gson gson = new Gson();
        HashMap<String, Object> payload = new HashMap<>();
        HashMap<String, String> data = new HashMap<>();

        data.put("title", TITLE);
        data.put("body", MESSAGE);

        payload.put("message", new HashMap<String, Object>() {{
            put("token", DEVICE_TOKEN);
            put("data", data);
        }});

        return gson.toJson(payload);
    }
}

 

  1_000 10_000 100_000
StringBuilder 구조 걸린 시간 1.487 ms 8.9012 ms 44.8641 ms
HashMap 걸린 시간 108.4761 ms 212.895 ms 534.303 ms

 

StringBuilder을 사용할 경우 HashMap 보다 11배 향상 되었음을 확인할 수 있었습니다.

하지만 여기서 StringBuilder 보다는 HashMap이 유지보수 시 구조적으로 파악이 더 쉽지 않을까 하는 생각도 했습니다.

그러나 fcm이   HTTP v1 API방식으로 바꾸기 전까지, 변수가 추가되거나 바뀐 경우가 없었습니다. 따라서 향후에 데이터 구조를 바꿀 가능성은 낮다고 판단하여 성능을 높일 수 있는 StringBuilder로 코드를 변경하였습니다.