inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

173만명의 커뮤니티!! 함께 토론해봐요.

/singup api 구현 에러

미해결

처음하는 파이썬 백엔드 FastAPI 입문 (FastAPI부터 비동기 SQLAlchemy까지) [풀스택 Part1-2]

안녕하세요. /signup api 구현시 500 error가 나서 문의드립니다. 테스트 해보니, plain_text로 bcrypt 변환 없이 바디에 적은 password 그대로 디비 저장시에는 문제가 없는데 hashed_password로 변환시에 해당 에러가 발생합니다. 확인부탁드립니다. 감사합니다.

  • python
  • mvc
  • sqlalchemy
  • FastAPI
  • backend
댓글 1 좋아요 0 조회수 265

pdf 파일 12_SQLALCHEMY_ADVANCE 소스파일이 다릅니다

미해결

처음하는 파이썬 백엔드 FastAPI 입문 (FastAPI부터 비동기 SQLAlchemy까지) [풀스택 Part1-2]

가장 첫번째 페이지 데이터 검색 (읽기) 에 나와있는 코드와 링크를 누르고 깃허브에 들어가서 나오는 코드가 다릅니다 . 수정부탁드립니다

  • python
  • mvc
  • sqlalchemy
  • FastAPI
  • backend
대성 댓글 2 좋아요 0 조회수 276

실수도 DB todo테이블을 삭제했습니다

미해결

실전! FastAPI 입문

contents내용을 수정하려다가 꼬여서 drop명령어로 새로 테이블을 작성하고 할려했는데 테이블을 그대로 만들었는데 uvicorn main:app 이 명령어를 수행하면 500internal error가 뜹니다. ㅠㅠ 어디서부터 뭘 수정해야 할지 모르겠는데 다시 파이썬프로젝트와 DB를 연결할수 있게 할수 있는방법 있을까요??

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
김지원 댓글 3 좋아요 1 조회수 316

섹션3 testcode get_단일조회 api

해결됨

실전! FastAPI 입문

여기까지 코드 잘 따라서 작성하였고 #200부분을 잘 작동합니다. #404 부분코드를 삭제하고 실행하면 잘 돌아가는데 404부분 코드를 작성하니까 48번째 줄에서 자꾸 오류가 뜨는데 해결방법이 있을까요??

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
김지원 댓글 1 좋아요 0 조회수 331

혹시 나중에 Django 강의도 내실건가요??

미해결

처음하는 파이썬 백엔드 FastAPI 입문 (FastAPI부터 비동기 SQLAlchemy까지) [풀스택 Part1-2]

Flask도 내시고, fastAPI도 내셨으면 Django도 내실거 같아서요.

  • python
  • mvc
  • sqlalchemy
  • FastAPI
  • backend
세상의끝 댓글 1 좋아요 0 조회수 286

proxy 사용 질문

미해결

파이썬 동시성 프로그래밍 : 데이터 수집부터 웹 개발까지 (feat. FastAPI, async, await)

안녕하세요. 사내에서 강의를 듣다보니 proxy를 타도록 되어 있습니다. basic-fetcher에서는 아래 두가지 방식이 모두 동작하는데요. os.environ["HTTP_PROXY"] = "http://xxx..." os.environ["HTTPS_PROXY"] = "http://xxx..." os.environ["PYTHONHTTPSVERIFY"] = "0" def fetcher(session, url): with session.get(url, proxies=proxies, verify=False) as respose: return respose.text coroutine-fetcher 에서는 ClientSession()에서 두가지 모두 오류가 납니다. 1번 async def fetcher(session, url): async with session.get(url, proxies=proxies, verify=False) as respose: return await respose.text() async def main(): urls = ["https://naver.com", "https://google.com", "https://instagram.com"] async with aiohttp.ClientSession() as session: result = await fetcher(session, urls[0]) print(result) 2번 async def fetcher(session, url): async with session.get(url, verify=False) as respose: return await respose.text() async def main(): urls = ["https://naver.com", "https://google.com", "https://instagram.com"] async with aiohttp.ClientSession() as session: result = await fetcher(session, urls[0]) print(result) 해결방법과 함께 proxy 환경에서는 프록시 정보를 어떤 구조로 가지고 있어야 효율적일지 문의드립니다.

  • python
  • 동시성
  • FastAPI
공부벌레 댓글 1 좋아요 0 조회수 290

BackgroundTask 인자값 전달

해결됨

실전! FastAPI 입문

안녕하세요 강사님 강의 재밌게 잘보고 있습니다 BackGround task 강의에서 궁금한게 생겨서 질문 남깁니다. 아래는 verify_otp_handler 의 선언부인데 def verify_otp_handler( request : VerifyOTPRequest, background_tasks : BackGroundTasks ... ) backgound_tasks 라는 객체는 DI로 받지도 않고 있는데 어떻게 함수 내부에서 사용될 수 있는지가 궁금합니다. request 라는 객체는 request 헤더부에서 넘어온 값이 프레임워크에 의해서 VerifyOTPRequest로 전처리되거라 어디서부터 왔는지 그 출처를 알겠지만, backgound_tasks는 DI도 되지 않았고 헤더부에서 넘어온 것도 아닌데 어떻게 생성된건지 궁금합니다.

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
유준모 댓글 1 좋아요 1 조회수 226

docker mysql 연동

미해결

실전! FastAPI 입문

docker에서는 mysql에 정상적으로 접속이 되는데 python console을 통해서 연결을 시도하면 sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2059, "Authentication plugin 'b'auth_gssapi_client'' not configured") 다음 에러가 나옵니다. select host, user from mysql.user where user= 'root'; 쿼리를 통해서 user 정보를 확인해보면 정상적으로 user가 셋팅 되어있습니다.

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
jino 댓글 2 좋아요 0 조회수 1091

pytest는 어떻게 모킹함수를 인지하는지

해결됨

실전! FastAPI 입문

안녕하세요 강사님 강의 정말 재밌게 보고 있습니다. 수강 도중 궁금한 내용이 생겨 질문 남깁니다. mocker.patch("main.get_todo_by_todo_id", return_value=Todo(id=1,contents="todo",is_done=True)) 여기서 인자값으로 문자열 main.get_todo_by_todo_id 를 전달했는데, pytest는 어떻게 저 함수를 모킹대상으로 인지하나요? 개념적으로 설명해주시면 감사합니다~!! response = client.patch("/todos/1", json={"is_done":False}) 해당 api 를 호출하고 실행하는 과정에서 main_get_todo_by_todo_id 함수를 호출할 때 어떻게 모킹처리가 되는지 궁금합니다.

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
유준모 댓글 1 좋아요 0 조회수 333

odm 관련

미해결

배달앱은 어떻게 내 주변의 맛집을 찾을까?

혹시 강사님께서 beanie와 같은 odm 라이브러리를 사용하지 않고, motor 드라이브와 data class로 구현한 이유가 궁금합니다. 추가적으로 entity를 pydantic model이 아닌 data class로 구현하신 이유가 궁금해요

  • python
  • mongodb
  • FastAPI
daniel 댓글 3 좋아요 0 조회수 502

타입힌트 질문

해결됨

실전! FastAPI 입문

안녕하세요 강사님 강의 듣는 도중 조금 궁금한 내용이 생겨서 질문드립니다. 코드에서 아래와 같이 타입힌트를 사용하는 경우가 있는데 def done(self) -> "Todo": self>is done = True ~ 왜 Todo에 "" 를 감싸주시는걸까요?? 그냥 Todo 라고 타입힌트를 줘도될거 같은데 이유가 궁금해서요

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
유준모 댓글 1 좋아요 0 조회수 318

코드 질문

해결됨

실전! FastAPI 입문

안녕하세요. 파이썬 2.7만 주로 쓰다 오랜만에 쓰니 좀 헷갈리는게 있어 질문드립니다. encoding: str = "UTF-8" service 작성하시면서 설명해주신 코드인데, encoding = "UTF - 8" 이렇게 써도 encoding 변수에 뭐가 들어가는지 바로 확인이 되는데 굳이 : str 과 타입을 명시할 이유가 있나요 ? 이렇게 작성하였을 때 왜 이렇게 작성하고 어떠한 장점이 있는지 궁금합니다. 비슷한 질문으로 def save_user(self, user: User) -> User: 다음과 같이 반환 타입을 적어줘야 하는 것도 설명 부탁드립니다. 작성하면서도 이걸 굳이 적어줘야 하는지가 궁금해서 질문드립니다.

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
인프린 댓글 1 좋아요 0 조회수 276

router testcode 문의

해결됨

실전! FastAPI 입문

router test code 에서 test_ main.py 파일에 api.todo라고 main을 바꾸어 줬는데 import를 따로 안 시켜줘도 되나요 ? main.py 는 from api import todo로 import 시키는데 test_ main.py 는 안 그래도 되는지 궁금합니다.

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
인프린 댓글 1 좋아요 0 조회수 316

docker 설치 에러

해결됨

실전! FastAPI 입문

안녕하세요. 윈도우 os 사용중이고 docker desktop 설치 시 이후 데몬이 활성화가 안되어 질문드립니다. 데몬 활성화를 위해서는 가상화 지원이 되어야 하는데, bios 세팅 및 os 가상화 설정을 모두 하여서 작업관리자에서 가상화:사용 이라고 까지 표기가 되어도 하이퍼바이저 지원이 안된다는 에러가 나옵니다. wsl 설치 또한 가상화가 지원이 안된다는 에러와 함께 설치가 안되어 구글링 이후 문의 드립니다.

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
인프린 댓글 1 좋아요 0 조회수 343

uvicorn 실행 에러

해결됨

실전! FastAPI 입문

안녕하세요. 학습 도중 재부팅하여 다시 uvicorn 실행하려고 하니 ERROR: Error loading ASGI app. Could not import module "main". 다음과 같은 메세지가 나오면서 fast api 앱이 실행이 안되는데 이유가 궁금합니다.

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
인프린 댓글 1 좋아요 0 조회수 1563

src 디렉토리를 기본으로 설정하는 방

해결됨

실전! FastAPI 입문

from main import app이 아닌 from src.main import app을 하게 되면 에러가 발생합니다. src. 을 생략할 수 있도록 하는 방법이 없을까요

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
fade 댓글 2 좋아요 0 조회수 404

session.scalar(select(1)) 에러 발생 질문입니다.

해결됨

실전! FastAPI 입문

에러명은 다음과 같습니다. 도커 설정은 이상이 없는데 어디서 문제가 생긴걸까요 pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '127.0.0.1' ([WinError 10061] 대상 컴퓨터에서 연결을 거부했으므로 연결하지 못했습니다)") The above exception was the direct cause of the following exception:

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
fade 댓글 1 좋아요 0 조회수 308

DATABASE_URL 상수변수 질문

해결됨

실전! FastAPI 입문

도커를 이용해 컨테이너를 실행시킬 때 root 사용자에 대해서 todos라는 비밀번호를 갖도록 설정을 해주었는데, DATABASE_URL = "mysql+pymysql://root:todos@127.0.0.1:3306/todos" 상수 변수를 이용해 데이터베이스 연결 시도 시, (1045, "Access denied for user 'root'@ 'localhos t' (using password: YES)") 이런 에러가 발생합니다. 비밀번호를 설정하였는데도 왜 이렇게 발생하는 건가요?

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
qpal1zm23 댓글 1 좋아요 0 조회수 268

(실습) ORM 적용 - HTTP Response 처리

미해결

실전! FastAPI 입문

from typing import List from fastapi import FastAPI, Body, HTTPException, Depends from pydantic import BaseModel from sqlalchemy.orm import Session from database.orm import ToDo from database.connection import get_db from database.repository import get_todos from schema.response import ListToDoResponse, ToDoSchema app = FastAPI() @app.get("/") def heath_check_handler(): return {"ping": "pong"} todo_data = { 1: { "id": 1, "contents": "실전! FastAPI 섹션 0 수강", "is_done": True, }, 2: { "id": 2, "contents": "실전! FastAPI 섹션 1 수강", "is_done": False, }, 3: { "id": 3, "contents": "실전! FastAPI 섹션 2 수강", "is_done": False, }, } @app.get("/todos", status_code=200) def get_todos_handler( order: str | None = None, session: Session = Depends(get_db), ) -> ListToDoResponse: todos: List[ToDo] = get_todos(session=session) if order and order == "DESC": return ListToDoResponse( todos=[ToDoSchema.from_orm(todo) for todo in todos[::-1]] ) return ListToDoResponse( todos=[ToDoSchema.from_orm(todo) for todo in todos] ) @app.get("/todos/{todo_id}", status_code=200) def get_todo_handler(todo_id: int): todo = todo_data.get(todo_id) if todo: return todo raise HTTPException(status_code=404, detail="ToDo Not Found") class CreateToDoRequest(BaseModel): id: int contents: str is_done: bool @app.post("/todos", status_code=201) def create_todo_handler(request: CreateToDoRequest): todo_data[request.id] = request.dict() return todo_data[request.id] @app.patch("/todos/{todo_id}", status_code=200) def update_todo_handler( todo_id: int, is_done: bool = Body(..., embed=True) ): todo = todo_data.get(todo_id) if todo: todo["is_done"] = is_done return todo raise HTTPException(status_code=404, detail="ToDo Not Found") @app.delete("/todos/{todo_id}") def delete_todo_handler(todo_id: int): todo = todo_data.pop(todo_id, None) if todo: return raise HTTPException(status_code=404, detail="ToDo Not Found") from typing import List from pydantic import BaseModel class ToDoSchema(BaseModel): id: int contents: str is_done: bool class Config: orm_mode = True class ListToDoResponse(BaseModel): todos: List[ToDoSchema] from typing import List from sqlalchemy import select from sqlalchemy.orm import Session from database.orm import ToDo def get_todos(session: Session) -> List[ToDo]: return list(session.scalars(select(ToDo))) from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker DATABASE_URL = "mysql+pymysql://root:todos@127.0.0.1:3306/todos" engine = create_engine(DATABASE_URL, echo=True) SessionFactory = sessionmaker(autocommit=False, autoflush=False, bind=engine) def get_db(): session = SessionFactory() try: yield session finally: session.close() from sqlalchemy import Boolean, Column, Integer, String from sqlalchemy.orm import declarative_base Base = declarative_base() class ToDo(Base): __tablename__ = 'todo' id = Column(Integer, primary_key=True, index=True) contents = Column(String(256), nullable=False) is_done = Column(Boolean, nullable=False) def __repr__(self): return f"ToDo(id={self.id}, contents={self.contents}, is_done={self.is_done})" 에러가 납니다. 파이썬 콘솔 <input>:1: PydanticDeprecatedSince20: The from_orm method is deprecated; from schema.response import ToDoSchema from datagbase.orm import ToDo Traceback (most recent call last): File "C:\Program Files\JetBrains\PyCharm 2023.3.2\plugins\python\helpers\pydev\pydevconsole.py", line 364, in runcode coro = func() ^^^^^^ File "<input>", line 1, in <module> File "C:\Program Files\JetBrains\PyCharm 2023.3.2\plugins\python\helpers\pydev\_pydev_bundle\pydev_import_hook.py", line 21, in do_import module = self._system_import(name, args, *kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ModuleNotFoundError: No module named 'datagbase' from database.orm import ToDo todo = ToDo(id=100, contents="test", is_done=True) ToDoSchema.from_orm(todo) <input>:1: PydanticDeprecatedSince20: The from_orm method is deprecated; set model_config['from_attributes']=True and use model_validate instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ Traceback (most recent call last): File "C:\Program Files\JetBrains\PyCharm 2023.3.2\plugins\python\helpers\pydev\pydevconsole.py", line 364, in runcode coro = func() ^^^^^^ File "<input>", line 1, in <module> File "C:\Users\manag\pyProject\todos\Lib\site-packages\typing_extensions.py", line 2499, in wrapper return arg(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\pydantic\main.py", line 1126, in from_orm raise PydanticUserError( pydantic.errors.PydanticUserError: You must set the config attribute from_attributes=True to use from_orm http://localhost:8000/docs#/default/get_todos_handler_todos_get 접속시 터미널 INFO: 127.0.0.1:56312 - "GET /docs HTTP/1.1" 200 OK INFO: 127.0.0.1:56312 - "GET /openapi.json HTTP/1.1" 200 OK 2024-01-23 16:06:15,108 INFO sqlalchemy.engine.Engine BEGIN (implicit) 2024-01-23 16:06:15,109 INFO sqlalchemy.engine.Engine SELECT todo.id, todo.contents, todo.is_done FROM todo 2024-01-23 16:06:15,109 INFO sqlalchemy.engine.Engine [cached since 826.5s ago] {} 2024-01-23 16:06:15,113 INFO sqlalchemy.engine.Engine ROLLBACK INFO: 127.0.0.1:56312 - "GET /todos HTTP/1.1" 500 Internal Server Error ERROR: Exception in ASGI application Traceback (most recent call last): File "C:\Users\manag\pyProject\todos\Lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 404, in run_asgi result = await app( # type: ignore[func-returns-value] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 84, in call return await self.app(scope, receive, send) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\fastapi\applications.py", line 1054, in call await super().__call__(scope, receive, send) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\applications.py", line 123, in call await self.middleware_stack(scope, receive, send) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\middleware\errors.py", line 186, in call raise exc File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\middleware\errors.py", line 164, in call await self.app(scope, receive, _send) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in call await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\_exception_handler.py", line 64, in wrapped_app raise exc File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app await app(scope, receive, sender) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 762, in call await self.middleware_stack(scope, receive, send) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 782, in app await route.handle(scope, receive, send) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 297, in handle await self.app(scope, receive, send) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 77, in app await wrap_app_handling_exceptions(app, request)(scope, receive, send) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\_exception_handler.py", line 64, in wrapped_app raise exc File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app await app(scope, receive, sender) File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 72, in app response = await func(request) ^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\fastapi\routing.py", line 299, in app raise e File "C:\Users\manag\pyProject\todos\Lib\site-packages\fastapi\routing.py", line 294, in app raw_response = await run_endpoint_function( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\fastapi\routing.py", line 193, in run_endpoint_function return await run_in_threadpool(dependant.call, **values) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\concurrency.py", line 40, in run_in_threadpool return await anyio.to_thread.run_sync(func, *args) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\anyio\to_thread.py", line 56, in run_sync return await get_async_backend().run_sync_in_worker_thread( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\anyio\_backends\_asyncio.py", line 2134, in run_sync_in_worker_thread return await future ^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\anyio\_backends\_asyncio.py", line 851, in run result = context.run(func, *args) ^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\src\main.py", line 48, in get_todos_handler todos=[ToDoSchema.from_orm(todo) for todo in todos] ^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\typing_extensions.py", line 2499, in wrapper return arg(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^ File "C:\Users\manag\pyProject\todos\Lib\site-packages\pydantic\main.py", line 1126, in from_orm raise PydanticUserError( pydantic.errors.PydanticUserError: You must set the config attribute from_attributes=True to use from_orm

  • python
  • 리팩토링
  • orm
  • FastAPI
  • pytest
네오스카이 댓글 2 좋아요 0 조회수 1277

AWS Lightsail SSL handshake failed 오류 문의드립니다.

미해결

파이썬 동시성 프로그래밍 : 데이터 수집부터 웹 개발까지 (feat. FastAPI, async, await)

AWS Lightsail에 git clone을 하고, 콜렉터스 북북이 초기 페이지까지는 잘 들어가졌는데,검색어를 입력하고 엔터를 하면 오랜 시간이 지나고 아래와 같은 오류문이 나옵니다. INFO: 211.234.195.243:54254 - "GET /search?q=Sss HTTP/1.1" 500 Internal Server ErrorERROR: Exception in ASGI application... <ServerDescription ('ac-ljyn7zm-shard-00-02.v7fc0ev.mongodb.net', 27017) server_type: Unknown, rtt: None, error=AutoReconnect("SSL handshake failed: ac-ljyn7zm-shard-00-02.v7fc0ev.mongodb.net:27017: [('SSL routines', 'ssl3_read_bytes', 'tlsv1 alert internal error')] (configured timeouts: socketTimeoutMS: 20000.0ms, connectTimeoutMS: 20000.0ms)")>]> 이런 오류가 나오면서 Internal Server Error가 뜹니다.혹시 어떻게 해결을 해야할지 알 수 있을까요?

  • python
  • 동시성
  • FastAPI
송의석 댓글 1 좋아요 0 조회수 523

인기 태그

인프런 TOP Writers

주간 인기글