기술블로그

✅ Flask에서 JSON 응답 순서 보장하기 (OrderedDict 활용)

Chansman 2025. 4. 18. 13:34

Flask에서 JSON 응답 순서 보장하기 (OrderedDict 활용)

문제 상황

Flask에서 jsonify를 사용하여 JSON 데이터를 반환할 때, 서버 측에서 데이터의 순서를 지정해도 JSON 응답을 받았을 때 순서가 뒤바뀌는 문제가 발생할 수 있습니다. JSON은 객체의 순서를 보장하지 않기 때문에, 브라우저나 서버에서 순서대로 데이터를 반환하지 않는 문제가 생깁니다.

이 문제를 해결하기 위해, **OrderedDict**와 **json.dumps()**를 사용하여 순서 보장을 명시적으로 설정할 수 있습니다.


1. 전체 코드 흐름

목표: Flask에서 JSON 데이터를 반환할 때, result가 먼저 오고 그 뒤에 data가 오도록 순서를 보장합니다.

from flask import Flask, jsonify
from collections import OrderedDict
import json

app = Flask(__name__)

@app.route('/api/v1/feeds', methods=['GET'])
def show_all_feeds():
    # 순서대로 데이터 설정
    data = OrderedDict([
        ('result', 'success'),
        ('data', OrderedDict([
            ('feed1', 'data1'),
            ('feed2', 'data2')
        ]))
    ])
    
    # JSON으로 변환하여 반환
    response = app.response_class(
        response=json.dumps(data, ensure_ascii=False, sort_keys=False),
        status=200,
        mimetype='application/json'
    )
    return response

if __name__ == '__main__':
    app.run(debug=True)

2. 코드 설명 및 수정된 부분

a. OrderedDict 사용

  • 목적: Python의 기본 dict는 삽입 순서를 보장하지 않지만, OrderedDict는 데이터를 삽입한 순서대로 반환합니다. 이를 사용하여 서버에서 데이터를 반환할 때 순서를 명확히 설정할 수 있습니다.
  • 수정된 코드: 데이터는 **OrderedDict**에 저장되고, **result**가 먼저 나오도록 설정됩니다. OrderedDict는 파이썬 딕셔너리와 동일하게 작동하지만 순서가 보장된다는 특징이 있습니다.
data = OrderedDict([
    ('result', 'success'),
    ('data', OrderedDict([
        ('feed1', 'data1'),
        ('feed2', 'data2')
    ]))
])
  • 여기서 ('data', OrderedDict([ ... ])) 부분은 Python의 딕셔너리 형식으로 **'data'**라는 키와 그 값을 연결하고 있습니다.
  • Python에서 **OrderedDict([('data', {...})])**와 같이 데이터를 정의할 때, 실제로 **콜론(:)**을 사용하여 **'data': {}**와 같이 키와 값을 구분합니다. 하지만, JSON으로 변환할 때 이 데이터가 **data:**와 같은 형태로 나타나는데, 이는 JSON 문법에 맞는 형식이기 때문입니다.
  • 왜 두 번 사용하나요?
    1. 첫 번째 OrderedDict:
      • result와 data는 최상위 레벨의 키-값 쌍입니다. result는 단순한 문자열 값을, data는 또 다른 딕셔너리로 묶어주고 있습니다. 여기서 **data**는 **OrderedDict**로 묶어서 중첩된 데이터를 보관합니다.
    2. 두 번째 OrderedDict:
      • data는 또 다른 중첩된 딕셔너리로, **feed1**과 **feed2**라는 항목을 가지고 있으며, 각각의 값은 "data1"과 "data2"입니다.
      • 이 내부에서 또 OrderedDict를 사용한 이유는 순서를 보장하면서 두 개의 항목을 저장하기 위함입니다. 순서가 중요하다면 중첩된 항목들도 순서대로 반환되어야 하기 때문에 OrderedDict를 사용합니다.

b. json.dumps() 사용

  • 목적: jsonify()는 기본적으로 키 정렬을 활성화시키기 때문에, 순서가 변경될 수 있습니다. 이 문제를 해결하기 위해, json.dumps()에서 sort_keys=False 옵션을 사용하여 키 정렬을 비활성화합니다.
  • 수정된 코드: json.dumps()는 sort_keys=False로 설정되어 JSON 응답의 키 정렬을 방지하고, 입력된 순서대로 반환합니다.
response = app.response_class(
    response=json.dumps(data, ensure_ascii=False, sort_keys=False),
    status=200,
    mimetype='application/json'
)

c. ensure_ascii=False 설정

  • 목적: ensure_ascii=False는 JSON 데이터에 한글 등의 비 ASCII 문자가 포함될 때, 그 문자가 유니코드로 출력되는 것을 방지하고 원본 그대로 출력하도록 합니다.
  • 수정된 코드: json.dumps()에서 ensure_ascii=False를 설정하여 한글이나 다른 비 ASCII 문자가 제대로 출력되도록 합니다.
response=json.dumps(data, ensure_ascii=False, sort_keys=False)

3. 결과 및 예상되는 응답

JSON 응답 예시

이 코드를 실행한 후 http://127.0.0.1:5000/api/v1/feeds URL에 접근하면, 아래와 같은 JSON 응답을 받을 수 있습니다:

{
    "result": "success",
    "data": {
        "feed1": "data1",
        "feed2": "data2"
    }
}

설명:

  • **result**가 먼저 오고 그 뒤에 **data**가 나오는 순서대로 JSON 응답이 반환됩니다.
  • feed1과 feed2는 data 객체 안에 들어가며, 데이터의 순서가 정확히 유지됩니다.

4. 흐름별 설명

  1. Flask 애플리케이션 설정: Flask 애플리케이션을 설정하고, /api/v1/feeds 경로에 대한 GET 요청을 처리하는 엔드포인트를 만듭니다.
  2. 데이터 순서 설정: OrderedDict를 사용하여 **result**와 data 순서를 명시적으로 설정합니다. 데이터가 삽입된 순서대로 JSON 객체를 만들 수 있습니다.
  3. JSON 직렬화: json.dumps()로 데이터를 직렬화하여 JSON 형식으로 반환합니다. sort_keys=False를 사용하여 정렬되지 않은 순서대로 반환되도록 합니다.
  4. 응답 반환: jsonify() 대신 json.dumps()로 변환된 데이터를 반환합니다. 이를 통해 순서를 보장하면서 JSON 데이터를 클라이언트에 반환합니다.

5. 결론

  • OrderedDict를 사용하여 데이터를 순서대로 처리하고, json.dumps(sort_keys=False)를 사용하여 JSON 응답에서 순서가 변경되지 않도록 할 수 있습니다.
  • Flask에서 JSON 순서 보장 문제를 해결하기 위해 jsonify() 대신 json.dumps()를 사용한 방법입니다.