FastAPI 스터디 준비 - 4주차 의존성 주입(Dependency Injection) 및 Depends

kanikim·2022년 2월 25일
2

FastAPI

목록 보기
4/5

제가 주로 활동하는 커뮤니티 HOLIX의 개발하는 사람들에서 다양한 개발자분들과 이야기를 나누어 보세요!

Dependency Injection이란 무엇인가?

Dependency Injection(의존성 주입)이라는 단어에서 보시다시피, 의존성이라는 단어가 중요합니다. 여기서 의존성은 클래스 사이에서 이루어지는 것이라 보면 편합니다. 즉, 클래스에 대한 의존성을 인터페이스화해서 코드의 유연성을 증대시키고, 클래스의 인스턴스를 외부에서 생성하여 주입하는 것을 의미합니다.


그렇다면 의존성(Dependency)란 무엇인가?

코드를 통해 예를 들어보겠습니다.

class Art:
	def __init__(self):
    	self.painter = "painter"
        self.time = datetime.now()

class NFT:
    def __init__(self):
    	self.art = Art()
        
def execute():
	nft = NFT()
    
if __name__ == "__main__":
	execute()

위의 코드에서 NFT클래스의 경우 Art객체를 생성하고 있음을 확인할 수 있습니다. 여기서 Art클래스에 변화가 생기면 NFT클래스에도 영향이 가지요. 즉, 여기서 NFT클래스와 Art클래스 사이에는 의존성이 있으며 강한 결합이 이루어져 있다고 볼 수 있습니다. 강한 결합은 좋지 않습니다. 하나의 코드가 바뀌면 타 객체에도 큰 영향이 갈 가능성이 크기 때문이지요.

의존성 주입은 이런 면에서 필요합니다. 강하게 결합되어 있는 두 클래스를 분리하고, 두 객체간의 관계를 정해줌으로서, 결합도를 낮추고 유연성을 확보하는 겁니다. 그 외에도 다음과 같은 상황에서 유리합니다.

  • DB 연결을 공유하는 경우
  • Application의 공통된 로직을 관리하는 경우
  • 인증 관리
  • 서로 다른 클래스의 Repository를 공유하는 경우

FastAPI에서의 의존성 주입

함수를 이용해 Depends 사용

FastAPI에서는 Depends라는 함수를 통해 의존성 주입을 관리합니다. 예제를 한번 보겠습니다.

from fastapi import Depends, FastAPI

app = FastAPI()

async def properties(offset: int = 0, limit: int = 100):
    return {"offset": offset, "limit": limit}

@app.get("/books/")
async def get_books(params: dict = Depends(properties)):
    return params

@app.get("/authors/")
async def get_authors(params: dict = Depends(properties)):
    return params

여기서 properties함수가 의존관계의 주체가 됩니다. 그리고 get_books(params: dict = Depends(properites))를 보면 Depends함수를 통해 params Path에 properties가 들어가 있는 것을 확인할 수 있습니다.

그렇다면 endpoint에서 /books/ url로 리퀘스트를 받으면 어떻게 될까요? 다음과 같은 플로우를 가지게 됩니다.

  • FastAPI는 의존관계에 있는 함수 offset과 limit parameter와 같이 properties를 불러옵니다.
  • 여기서 결과는 properties에서 처리되며 그 결과는 Dictionary타입입니다.
  • 결과적으로 path 함수에다가 그 결과를 할당합니다.

클래스를 이용해 Depends 함수 사용

from fastapi import Depends, FastAPI

app = FastAPI()

class Properties:
    def __init__(self, offset: int = 0, limit: int= 100):
        self.offset = offset
        self.limit = limit

@app.get("/fetch-books")
async def fetch_books(properties: Properties = Depends(Properties)):
    response = {}
    response.update({"offset": properties.offset})
    response.update({"limit": properties.limit})
    return response

한번 클래스로 의존성을 주입해보겠습니다. 여기서 보다싶이 init method에서 객체를 생성합니다. 여기서도 위와 같이, offset과 limit이 똑같이 할당되어 그 결과를 fetch_books함수에서 받습니다.


Sub-dependencies 다루기

from fastapi import Depends, FastAPI

app = FastAPI()

async def default_properties(limit: int):
    if limit == 0:
        return 5
    else:
        return limit

async def determine_properties(offset: int, limit: int = Depends(default_properties)):
    return {"offset": offset, "limit": limit}

@app.get("/fetch-authors")
async def fetch_authors(properties: dict = Depends(determine_properties)):
    return properties

위의 코드를 보면 default_properties -> determine_properties -> fetch_authors 순으로 의존성이 주입되는 것을 확인할 수 있습니다. 이러한 경우 FastAPI는 먼저 default_properties를 먼저 해결하고, 그 다음 determine 그리고 마지막으로 fetch_authors를 해결합니다.


Summary

이렇듯 의존성 주입은 디자인 패턴상에서 중요하고, 코드를 유지보수하기 편하게 만들어줍니다.

profile
Backend Developer

0개의 댓글