인프런 커뮤니티 질문&답변
patch 요청시 payload가 넘어가지 않습니다.
해결된 질문
작성
·
35
·
수정됨
0
아래 두 요청 코드에서 patch 요청 시 payload가 엔드포인트 함수에서 None으로 잡혀 model_dump()에서 오류가 발생합니다. 원인을 잘 모르겠습니다.
[오류]

[요청 코드]
@pytest.mark.parametrize("payload", [
{"display_name": "푸딩캠프"},
{"email": "hannal@example.com"},
{"display_name": "푸딩캠프", "email": "hannal@example.com"},
])
async def test_사용자가_변경하는_항목만_변경되고_나머지는_기존_값을_유지한다(
client_with_auth: TestClient, # 인증을 받은 클라이언트
payload: dict, # 클라이언트 요청 페이로드
host_user: User, # 클라이언트 사용자
):
# 현재 사용자 정보를 보관한다.
before_data = host_user.model_dump()
response = client_with_auth.patch("/account/@me", json=payload)
# (...)async def test_비밀번호_변경_시_해싱_처리한_비밀번호가_저장되어야_한다(
client_with_auth: TestClient,
host_user: User,
db_session: AsyncSession,
):
before_data = host_user.hashed_password
payload = {
"password": "new_password",
"password_again": "new_password",
}
response = client_with_auth.patch("/account/@me", json=payload)
# (...)
[엔드포인트]
@router.patch("/@me", response_model=UserDetailOut)
async def update_user(
user: CurrentUserDep,
session: DbSessionDep,
payload: UpdateUserPayload = Body(...),
) -> User:
updated_data = payload.model_dump(exclude_none=True, exclude={"password", "password_again"})
stmt = update(User).where(User.id == user.id).values(**updated_data)
await session.execute(stmt)
await session.commit()
await session.refresh(user)
return user
[스키마]
class UpdateUserPayload(SQLModel):
display_name: str | None = Field(default=None, min_length=4, max_length=40)
email: EmailStr | None = Field(default=None, max_length=128)
password: str | None = Field(default=None, min_length=8, max_length=128)
password_again: str | None = Field(default=None, min_length=8, max_length=128)
@model_validator(mode="after")
def check_all_fields_are_none(self) -> Self:
if not self.model_dump(exclude_none=True):
raise ValueError("최소 하나의 필드는 반드시 제공되어야 합니다.")
return self
@model_validator(mode="after")
def verify_password(self) -> Self:
if self.password is not None or self.password_again is not None:
# 둘 중 하나라도 들어오면 둘 다 있어야 함
if not self.password or not self.password_again:
raise ValueError("비밀번호 변경 시 password와 password_again을 모두 제공해야 합니다.")
if self.password != self.password_again:
raise ValueError("비밀번호가 일치하지 않습니다.")
@computed_field
@property
def hashed_password(self) -> str | None:
if self.password:
return hash_password(self.password)
return None
[픽스처]
@pytest.fixture(autouse=True)
async def db_session():
dsn = "sqlite+aiosqlite:///:memory:"
engine = create_async_engine(dsn)
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.drop_all)
await conn.run_sync(SQLModel.metadata.create_all)
session_factory = create_session(engine)
async with session_factory() as session:
yield session
await conn.run_sync(SQLModel.metadata.drop_all)
await engine.dispose()
@pytest.fixture()
def fastapi_app(db_session: AsyncSession):
app = FastAPI()
include_routers(app)
async def override_use_session():
yield db_session
app.dependency_overrides[use_session] = override_use_session
return app
@pytest.fixture()
async def host_user(db_session: AsyncSession):
user = account_models.User(
username="puddingcamp",
hashed_password=hash_password("testtest"),
email="puddingcamp@example.com",
display_name="푸딩캠프",
is_host=True,
)
db_session.add(user)
await db_session.flush()
await db_session.commit()
return user
@pytest.fixture()
def client_with_auth(fastapi_app: FastAPI, host_user: account_models.User):
payload = LoginPayload.model_validate({
"username": host_user.username,
"password": "testtest",
})
with TestClient(fastapi_app) as client:
response = client.post("/account/login", json=payload.model_dump())
assert response.status_code == status.HTTP_200_OK
auth_token = response.cookies.get("auth_token")
assert auth_token is not None
client.cookies.set("auth_token", auth_token)
yield client 답변 3
1
1
프로젝트 코드 전체요. github 에 repository로 관리하신다면, 그곳을 알려주셔도 좋고, local에서만 작업하셨다면 압축하여 전달해주시면 되어요. 파일을 내려받을 url 알려주세요. ( kay ✉hannal.net 으로 알려주세요. 🙂
흑흑 원인을 찾았습니다... ㅠㅠ 스키마 model_validator에 return를 빼먹었네요. "after" 옵션이라 모델 객체 생성 후 검증 과정에서 return이 없어 payload가 None으로 찍힌 것 같습니다.
0
안녕하세요, 인프런 AI 인턴입니다. kyo lee님.
질문해주신 패치 요청 시 페이로드가 None으로 처리되어 발생하는 문제에 대해 설명드리겠습니다.
원인 및 해결 방법
데이터 유효성 검사: 문제가 발생하는 부분은
update_user엔드포인트에서payload가None이 되는 경우입니다. 이는Body(...)를 통해 요청 본문을 읽어오도록 되어 있는데, 실제 클라이언트 측에서json=payload형태로 데이터를 전송하고 있으므로 이 경우payload의 스키마가 잘못되었거나 FastAPI에서 본문을 제대로 읽어오지 못하는 경우일 수 있습니다.모델 매칭:
UpdateUserPayload스키마가 정확히 요청의 데이터 구조와 일치하는지 확인해야 합니다. 예를 들어,display_name,email,password,password_again등의 필드가 올바르게 정의되어 있는지 확인하세요.Request Body Parsing: 만약
Body(...)가 제대로 작동하지 않는 것 같다면,payload를 직접 JSON으로 전송하고 올바르게 파싱되는지 확인해야 합니다. 혹시 JSON 형식이 잘못되어 수신되지 않는 것은 아닌지 확인해보세요.
아래는 체크리스트를 제시합니다:
UpdateUserPayload정의: 이 데이터 모델이 요청의 데이터 구조와 정확히 일치하는지 다시 확인합니다.
- FastAPI Configuration: FastAPI 애플리케이션에서 JSON 요청 본문을 읽어오도록 설정이 되어 있는지 확인합니다.
- 테스트 데이터 검증: 테스트에서 사용 중인 데이터
payload의 구조가 모델의 필드와 일치하는지 확인합니다.
추가적인 링크
유사한 문제가 발생할 수 있는 사례에 대한 링크를 살펴보세요:
- FastAPI Request Body Management – Inflearn 기존 질문
이는 일반적인 FastAPI 엔드포인트에서의 Request Body 파싱 문제일 수 있습니다. 따라서, 위의 해결책들을 단계적으로 확인하여 문제가 되는 부분을 수정해 보시기 바랍니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.





테스트 코드 말씀이실까요? 아래 우선 올려보겠습니다!