RFC 7725 한국어 번역: 법적 장애를 보고하는 HTTP 상태 코드

RFC 7725: 법적 장애를 보고하는 HTTP 상태 코드 요약 이 문서는 자원 접근이 법적 요구로 인해 거부될 때 사용하는 하이퍼텍스트 전송 프로토콜(HTTP) 상태 코드를 지정합니다. 1. 소개 이 문서는 서버 운영자가 법적 요구를 받아 특정 자원 또는 요청된 자원을 포함하는 자원 집합에 대한 접근을 거부해야 하는 상황에서 사용할 수 있는 HTTP 상태 코드를 지정합니다. 이 상태 코드는 법적 문제나 공공 정책 문제로 인해 서버 운영에 영향을 미치는 경우에 투명성을 제공하는 데 사용될 수 있습니다. 이러한 투명성은 운영자와 최종 사용자 모두에게 유익할 수 있습니다. 2. 요구 사항 이 문서에서 사용되는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 및 "OPTIONAL"이라는 단어는 [ RFC2119 ]에 따라 해석됩니다. 3. 451 법적 이유로 사용할 수 없음 이 상태 코드는 법적 요구로 인해 서버가 자원에 대한 접근을 거부하고 있음을 나타냅니다. 이 서버는 원 서버일 필요는 없습니다. 이러한 유형의 법적 요구는 일반적으로 ISP 및 검색 엔진의 운영에 가장 직접적인 영향을 미칩니다. 이 상태 코드를 사용하는 응답에는 법적 요구의 세부 사항(요구를 제기한 당사자, 적용되는 법률 또는 규정, 해당하는 사람 및 자원의 클래스)을 설명하는 내용이 응답 본문에 포함되어야 합니다. (SHOULD) 4. 차단하는 엔터티 식별 앞서 언급했듯이, 상태 451로 자원 접근 시도가 실패하면, 접근을 차단하는 엔터티가 원 서버일 수도, 아닐 수도 있습니다. 리소스 접근 경로에서 접근을 거부할 수 있는 다양한 엔터티가 있습니다. 법적 차단이 발생했을 때, 실제로 차단을...

나는 1시간만에 웹사이트 하나를 번역했다. (with claude api)

starlette 비공식 한국어 페이지

매우 고무적인 번역 성과가 있어서 공유해보려고 합니다.

starlette logo


지금 위에 있는 starlette 한국어 페이지, 번역하는데 얼마나 걸렸을까요?


1시간 걸렸습니다..


물론 starlette에 내용이 별로 없어서 절대적인 분량이 작긴 하지만, 1시간 안에 개발문서 전체를 번역했다는 것은 의미가 있다고 생각합니다.

지금부터 번역 과정을 설명드리겠습니다.


1. claude api를 알아보다.

claude는 anthropic에서 제공하는 LLM입니다. OPENAI의 GPT와 마찬가지로 api 형태로 호출하는 것을 지원합니다.

저는 claude의 성능을 테스트 해볼 겸 이 api를 사용해서 자동으로 문서를 번역해보고자 계획하였습니다.

우선 claude api 콘솔에 접속하시면 다음과 같은 화면이 나옵니다.



여기서 GET API KEY 항목에 들어가서 API KEY를 발급 받아주었습니다. 현재 5$에 해당하는 크레딧을 주고 있어서, 많은 작업을 하는 것이 아니면 api 호출에 대한 비용 부담은 많이 없습니다. 그러나 사용자가 추가로 5$이상 예치해야 rate limit을 넉넉하게 풀어주기 때문에 저는 추가로 예치하였습니다. 예치 방법은 settings 메뉴에 들어가서 billing을 눌러 카드 결제를 해주면 됩니다.

 

2. 시스템을 구축하다.

자동으로 문서를 번역하기 위한 시스템 구조입니다.


2개의 디렉토리와 3개의 파이썬 파일로 구성되어 있습니다.

  • input: claude가 작업할 내용물을 보관하는 디렉토리입니다.
  • output: claude가 완료한 작업물을 보관하는 디렉토리입니다.
  • main.py: 다른 모듈들을 불러오고 에러를 관리합니다.
  • log.py: 작업 성공 유무를 기록하는 logger입니다.
  • claude.py: claude api를 직접 호출하며, logger를 가져와 기록하고, output 디렉토리에 결과물을 저장합니다.
우선 claude.py 코드부터 보겠습니다.
pip로 anthropic과 python-dotenv를 설치해야 합니다.




from anthropic import Anthropic
from log import logger
from dotenv import load_dotenv
import os
import time


load_dotenv()

 

def api_call(max_tokens: int, system: str, model: str):
    input_list = os.listdir("./input")
    input_list.remove("instruction.json")
    api_key = os.getenv("CLAUDE_API_KEY")
    if api_key is None:
        raise KeyError("CLAUDE_API_KEY")
    client = Anthropic(
        api_key=api_key,
    )

 

    for i in input_list:
        with open(os.path.join("./input", i), "r", encoding="UTF8") as f:
            content = f.read()
            message = client.messages.create(
            max_tokens=max_tokens,
            system=system,
            messages=[
            {
            "role": "user",
            "content": content,
            }
            ],
            model=model,
        )
        write_output(message, i)
        time.sleep(2)


def write_output(message, file):
    if message.type == "message" and message.stop_reason == "end_turn":
        with open(os.path.join("./output", file), "w", encoding="UTF8") as f:
            f.write(message.content[0].text)
        logger.info(f"{file}에 대한 출력을 완료하였습니다.")
    else:
        logger.error(f"{file}에 대한 요청을 보냈으나, 정상적인 응답이 오지 않았습니다.")

 

api_call에 대한 코드 진행 과정 설명입니다.

  1. input 디렉토리를 조사합니다. 여기서 설정 json은 제외합니다(설정 json은 main 설명 때 같이 설명).
  2. 환경변수에서 api_key를 가져옵니다. .env 파일을 만들고 여기에 CLAUDE_API_KEY="발급 받은 키" 형식으로 작성해주시면 됩니다. 만약 수동으로 커맨드 라인에서 api key를 설정할 경우 python_dotenv는 필요없습니다. 혹은 코드에 직접 api key를 넣던가.
  3. 반복문을 돌며 input 디렉토리 안에 있는 파일들을 읽습니다. 이 파일들에는 프롬프트가 들어있습니다. 반복문 안에서, 각 파일의 프롬프트를 설정 파일에 있는 여러 값들과 함께 claude에게 요청을 보냅니다. 결과물을 output 디렉토리에 저장하는 함수를 호출하고, 잠시 기다립니다(잠시 기다리는 이유는 rate limit 때문입니다).
write_output에 대한 코드 진행 과정 설명입니다.
  1. claude에게 요청을 보낸 결과 객체의 type을 조회합니다. 그리고 stop_reason을 조사합니다. 이 둘은 claude api의 성공 여부를 확인하는데 중요합니다.
  2. 정상적인 응답이였다면, 응답의 내용을 output 디렉토리에 파일 형태로 넣습니다.
  3. 응답에 관계없이 로그를 기록합니다.

이 코드를 이해하기 위해서는 anthropic에서 제공하는 api 구조에 대한 이해가 필요합니다.
우선 api를 호출할 때는 아래 항목들을 구성해야 합니다.
  • model: claude 중 어떤 모델을 사용할 것인지 문자열 형태로 지정해주어야 합니다. 일반적으로 claude-3-5-sonnet-20240620를 추천합니다.
  • max_tokens: claude는 이 매개변수의 값을 넘어서는 응답을 할 수 없게 됩니다. claude-3-5-sonnet-20240620의 경우 8192가 최대입니다. (LLM의 토큰은 자연어의 단어와 비슷하지만 약간 다릅니다. 이에 유의해야 합니다)
  • system: 시스템 프롬프트를 정의합니다. 일반 프롬프트보다 우선되는 지시사항을 넣을 수 있습니다. 예를 들어 "한국어로 대답해줘" 등이 있습니다.
  • messages: claude에게 보낼 프롬프트를 설정합니다. 리스트 객체인데, 그 이유는 앞선 대화를 기억하는 방식을 구현하기 위함입니다(이 코드에서는 기억을 사용하지 않았습니다. 자세한 내용은 마지막에..). 리스트의 각 요소에는 role과 content key가 있는 딕셔너리입니다. role이 user인 경우 사용자의 메시지이며, assistant인 경우 claude의 메시지입니다. content는 프롬프트(role이 user인 경우), 응답(role이 assistant인 경우)을 의미합니다. 참고로 messages의 마지막 요소의 role이 항상 user인 필요는 없습니다. 만약 assistant로 되어 있어도, claude는 이를 미리 채워진 응답이라고 판단하고, 계속 이어서 답변할 수 있습니다.
위 코드에서 api 응답은 json 형태로 오며 message 변수에 담깁니다(anthropic 모듈이 자동으로 json이 파이썬 객체로 변환하는 것 참고). 자세한 내용은 아래와 같습니다.
  • type: api 응답의 성공 여부는 type 필드를 봐야 알 수 있습니다. 그냥 message인 경우 성공, error인 경우 api 자체가 실패하였다는 뜻입니다.
  • stop_reason: claude가 응답을 멈추고, 반환하기로 결정한 이유를 나타냅니다. end_turn인 경우 "그냥 끝나서"라는 뜻입니다. 이 필드를 보는 것도 중요한데, 토큰 제한을 초과하는 경우를 찾아내는데 필요하기 때문입니다. end_turn이 아니라 max_tokens인 경우 토큰 제한을 초과한 것입니다.
  • content: claude의 응답을 나타냅니다. 이 또한 리스트로 되어 있으며, 첫 요소의 text를 가져와서 claude의 응답 텍스트만  추출할 수 있습니다.

여기까지 핵심 모듈은 claude.py였습니다. 아래는 main.py입니다.



from log import logger
import json


try:
    with open("./input/instruction.json", "r", encoding="UTF8") as f:
        instruction = json.load(f)
    if str(instruction['module']).lower() == "none":
        pass
    elif str(instruction['module']).lower() == "claude":
        from claude import api_call
        api_call(instruction['info']['max_tokens'], instruction['info']['system'], instruction['info']['model'])
    else:
        raise ImportError(f"{instruction['module']}는 존재하지 않습니다.")


except OSError as e:
    logger.error(e)
except KeyError as e:
    logger.error(f"{e}는 instruction에 존재하지 않습니다.")
except ImportError as e:
    logger.error(e)

 

예외처리와 코드의 실행을 담당합니다. 여기서 중요한 것은 instruction.json입니다. 일종의 설정을 담고 있는 파일이며, 향후 이 시스템이 자동으로 돌아가게 할 확장성을 남겨두기 위해 이 구조를 사용하였습니다. 아래는 제가 사용한 instruction.json입니다.
{
  "module": "claude",
  "info": {
    "model": "claude-3-5-sonnet-20240620",
    "max_tokens": 4096,
    "system": "You need to translate development documentation from English into Korean for Korean developers. You need to be careful not to translate proper nouns or names incorrectly(Usually no translation is required), not to mess with the specified formatting, and not to break the original code (but you can translate natural language text within the code). You don't need to output the text that the translation is complete; recommend just outputting the translated document."
  }
}

마지막으로 log.py입니다. python 내장 logger를 사용하였으며, claude가 작업을 정상적으로 완료하였는지 확인할 수 있습니다.


import logging



logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

 

file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO)


formatter = logging.Formatter('%(asctime)s - %(filename)s - %(levelname)s - %(message)s')


file_handler.setFormatter(formatter)
logger.addHandler(file_handler)


3. 자료를 분리한다.

이 시스템에서 유일하게 사람 손이 필요한 부분은 input 디렉토리에 프롬프트가 있는 파일을 넣고, output 디렉토리에 있는 파일을 (번역 대상이 있는) 저장소로 옮기는 일입니다.
우선 원본 파일을 input 디렉토리에 넣는 일을 알아보겠습니다.
원본 파일이 rst이든, md이든, html이든 claude는 알아서 잘 형식을 깨트리지 않고 해줍니다. 다만 분량이 너무 길 경우 번역 완성하지 못할 수 있습니다. 이를 예방하기 위해 긴 파일을 적절하게 분리해야 합니다. 그러나 이 작업을 claude(LLM)에게 맡기는 것에는 무리가 있다고 판단하였습니다.
실험을 하나 진행했습니다. 목적을 설명해준 뒤 긴 파일, 짧은 파일 여러 개를 주고는 분리해달라고 요청하였습니다. 실험을 진행한 결과, claude는 주로 목차(마크다운의 경우 #, ## 등)를 기준으로 파일을 분리하였습니다. 구체적으로 적절한 token 개수에 대해 설명을 해주어도, 대체로 너무 짧게 분리하는 현상이 많았습니다(다만 너무 길게 분리하는 현상은 보이지 않았습니다). 짧게 여러 번 보내든, 길게 한 번 보내든 돈 나가는 건 똑같지만, 잘못하면 rate limit에 걸릴 수 있어서 저는 수동으로 파일을 하나씩 분리하였습니다.

시간이 넘친다면, 이 부분은 자동화할 수 있을 것으로 보입니다.


4. 결과를 취합한다.

시간이 넘치면 input 디렉토리에 자료를 넣는 과정은 자동화할 수 있습니다. 그러나 output 디렉토리의 결과물들을 합치는 과정은 상당히 어려울 것 같습니다. 이 시스템으로 starlette를 번역한 결과물에서 발생한 문제는 상대경로로 된 링크였습니다. 목차 등이 url과 직접적으로 연관되어 있는 경우, 목차를 번역하면 링크가 작동하지 않을 가능성이 아주 큽니다. 다행히 mkdocs로 된 starlette에서는 작동하지 않는 링크를 자동으로 찾아주기에 오류를 수정하는데 오래 걸리지 않았습니다(문서 내에 있는 영어 제목은 오류 수정의 흔적입니다)
이를 수정할 방법은, claude에게 전반적인 문서 구조를 알려주거나, 상대링크를 고려하라는 프롬프트 정도가 있을 것 같습니다. 그러나 다른 파일과 링크가 연결되어 있는 경우, 프롬프트가 많이 복잡해질 것 같습니다.


5. 웹사이트 완성!

input 디렉토리와 output 디렉토리에 대한 작업을 하는 시간(그리고 처음에 코드 작성 시간)을 제외하면 제가 실행 중 개입하지 않아도 되는 자동화를 이루어냈습니다. 그러나 생각해보아야 하는 부분들이 남아있습니다.

1. 비용

비용이 크지 않았습니다. 


코드 테스트하면서 api 호출한 금액을 제외하면 starlette 번역 금액은 딱 1$가 나왔습니다. 처음 시작할 때 비용 문제를 고민했었는데, 시간 절약을 생각하면 이 정도는 괜찮습니다.

2. 번역의 퀄리티

claude3.5는 gpt4에 버금가는 능력을 지니고 있습니다. 그러나 영어권 LLM인 관계로? 번역 시 아쉬운 점들이 조금 있었습니다.

claude의 결과물 중 하나입니다. 상당히 만족스럽지만, 저는 트레이스백traceback 또는 역추적이라고 번역했을 것 같습니다. 이건 어쩌면 제 개인 취향일 수도 있으며, claude의 결과를 제 스타일보다 선호하시는 개발자도 있을 수 있습니다. 그러나 저의 스타일은 '음역을 할 바에 원문을 쓴다'라서.. 
분명한 점은 claude는 어려운 한국어 어휘나 문장을 잘 이해합니다. 그러므로 프롬프트를 통해 이 사안을 개선할 수 있다는 여지가 있을 것 같습니다. 


댓글

이 블로그의 인기 게시물

우교예찬(愚校禮讚) - 8월 18일 완성안

RFC 7725 한국어 번역: 법적 장애를 보고하는 HTTP 상태 코드