ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 200 OK가 거짓말을 한다 — Seedance 2.0 fast API의 silent fallback을 잡아내는 법
    IT 2026. 5. 21. 21:00
    200 OK가 거짓말을 한다 — Seedance 2.0 fast API의 silent fallback을 잡아내는 법

    📡 이 글은 BytePlus Seedance 2.0 fast 모델을 API로 직접 호출해서 영상을 만드는 사용자를 위한 글입니다. ModelArk 콘솔에서 클릭으로 영상을 만드는 분들에게는 해당되지 않는 내용이 많습니다 — 콘솔 UI가 옵션을 입력 필드로 받기 때문에 형식 실수 자체가 일어날 일이 없으니까요. API 통합·자동화를 고민 중인 분이라면 시도착오 시간을 줄여줄 수 있습니다.

    200 OK가 돌아왔다, 그런데 결과가 이상하다

    우리 아이를 주인공으로 한 짧은 가족 영상을 만들어 보겠다며 BytePlus Seedance 2.0 fast API와 처음 싸운 날의 이야기입니다. 호출은 200 OK로 잘 돌아오는데, 만들어진 영상은 어딘가 자꾸 이상했습니다.

    • 분명히 7초짜리 클립을 만들려고 했는데 결과는 5초였습니다.
    • 16:9를 요청했는데 결과는 입력 사진 비율 그대로였습니다.
    • 음성도 같이 만들지 말라고 적었는데 모델이 자기 멋대로 만든 다른 언어의 음성이 들어가 있었습니다.

    "prompt가 잘못됐나? 이미지가 너무 작나? 모델이 약한가?" — 자연스럽게 의심이 그쪽으로 갑니다. prompt를 바꾸고, 이미지를 다시 자르고, 비싼 모델로 올려 다시 호출합니다. 그러기를 다섯 번. 다섯 번의 paid 생성이 모두 똑같이 이상하게 나옵니다. 비용도 아깝지만 진짜 아까운 건 한 사이클 90초씩 기다리며 머리만 굴린 시간입니다.

    원인은 prompt도 모델도 아니었습니다. 옵션을 전달한 형식 자체가 통째로 무시되고 있었을 뿐입니다.

    원인 — 옵션을 한 줄짜리 문자열로 보내고 있었다

    처음에 옵션을 이렇게 보내고 있었습니다.

    {
      "model": "seedance-2.0-fast",
      "content": [...],
      "parameters": "--duration 7 --resolution 720p --ratio 16:9"
    }
    

    직관적으로는 "CLI 인자처럼 한 줄에 모아 넣는 게 편하겠지" 싶었고, 어디 한 군데 가이드 문서에서 그런 식의 예제를 봐서 신뢰하고 그대로 따라 한 거였습니다. 그런데 진짜 API 스키마는 top-level JSON 필드를 직접 받습니다.

    {
      "model": "seedance-2.0-fast",
      "content": [...],
      "duration": 7,
      "ratio": "16:9",
      "generate_audio": false
    }
    

    parameters: "--duration 7 ..."는 API 입장에선 모르는 필드 하나일 뿐이라 통째로 무시됩니다. 무시한다고 400을 내려주는 친절한 API였으면 1초만에 발견했을 텐데, 그대로 200 OK + default 옵션으로 영상을 만들어서 돌려줍니다. 모든 옵션이 default(5초·입력 사진 비율·음성 자동 생성 ON)로 떨어진 결과가 바로 위에서 본 그 "이상한 영상"이었습니다.

    이런 동작을 silent fallback 이라고 부릅니다. 잘못된 입력을 명시적으로 거절하지 않고 조용히 default로 흘려보내는 패턴이죠. 사용자 입장에선 가장 디버깅하기 어려운 종류입니다.

    무엇이 문제인가 — 200이 두 가지를 동시에 의미한다

    이 함정이 위험한 건 같은 200 OK가 두 의미를 동시에 가린다는 점입니다.

    diagram

    다이어그램에서 보다시피, 같은 200 OK 응답이 두 의미를 동시에 가립니다 — 한 쪽은 "당신의 옵션이 적용됐다", 다른 쪽은 "당신의 옵션은 모르는 필드라 버렸지만 어쨌든 처리는 했다". 클라이언트 입장에선 응답 코드만 봐서는 둘을 구별할 수 없습니다. 결과 영상의 metadata를 일일이 뜯어볼 때까지 비대칭이 드러나지 않죠.

    해결 — "응답 코드"가 아니라 "응답 body"로 검증한다

    해법은 단순합니다. 첫 호출 한 건의 응답 body를 통째로 캡처해서 공식 docs sample과 거의 byte 단위로 비교하면 됩니다. 무료 quota가 있는 모델이라면 바로 거기서 1건 dry-run 호출을 돌리고, 본 production은 그 다음에 들어가는 게 안전합니다.

    자동화된 형태로는 이런 식의 작은 검증 함수가 있으면 좋습니다.

    def validate_seedance_response(submitted: dict, returned_meta: dict) -> list[str]:
        """제출한 옵션이 실제 응답에 반영됐는지 확인."""
        mismatches = []
    
        # duration 비교 (요청 7초 → 응답이 5초로 떨어졌으면 silent fallback)
        if submitted.get("duration") and returned_meta["duration"] != submitted["duration"]:
            mismatches.append(
                f"duration silently ignored: requested {submitted['duration']}, "
                f"got {returned_meta['duration']}"
            )
    
        # ratio 비교
        if submitted.get("ratio") and returned_meta["ratio"] != submitted["ratio"]:
            mismatches.append(
                f"ratio silently ignored: requested {submitted['ratio']}, "
                f"got {returned_meta['ratio']}"
            )
    
        # generate_audio=false였는데 결과에 audio track이 있으면 무시된 것
        if submitted.get("generate_audio") is False and returned_meta["has_audio"]:
            mismatches.append("generate_audio=false silently ignored: audio track present")
    
        return mismatches
    

    이 함수는 200 OK 이후에 호출해서, 요청과 응답의 메타데이터를 항목별로 비교합니다. 한 항목이라도 어긋나면 silent fallback이 일어났다는 신호로 보고 명시적인 에러로 격상합니다. 한 번만 만들어 놓으면 그 다음부터 새로운 옵션을 추가할 때마다 검증 한 줄씩 늘려가면 됩니다. 어떤 의미에서는 외부 API에 대해 내가 직접 만든 validation 레이어인 셈입니다.

    결과 — 시도착오 시간 회수

    이 검증을 미리 갖춰뒀더라면 다섯 번의 paid 호출과 그 사이 몇 시간의 가설 검정이 1분으로 줄었을 겁니다. body shape이 잘못됐다는 사실이 첫 호출에서 명시적으로 알람으로 떴을 테니까요. 직접 만들어 본 후 정리한 원칙은 세 가지입니다.

    • 첫 호출은 항상 body diff부터. 직접 작성한 요청 JSON과 docs sample을 키 단위로 비교한다.
    • 200 OK가 의심스러우면 응답이 의도 옵션을 그대로 반영했는지 확인한다. 길이·비율·audio 같은 메타 필드는 모델 응답에 다 들어 있다.
    • prompt를 의심하기 전에 contract를 의심한다. 파라미터 통째 무시가 가장 흔한 silent fallback 패턴이고, prompt를 만져 봐야 영원히 해결되지 않는다.

    내가 손으로 쓰는 코드와 마찬가지로, BytePlus Seedance 같은 외부 AI API도 silent fallback을 한다는 사실 자체를 가정으로 깔아두는 편이 결과적으로 시간을 가장 많이 회수해 줍니다.


    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.