본문 바로가기
Backend/Python

[FastAPI] SQL Alchemy 연결 오류 해결

by 천우산__ 2024. 6. 5.

1. DB 연결 주소 수정 필요

기존 프로젝트는 AWS RDS MySQL로 연결해서 구현하였으나, 프리티어 기간이 종료되었다.

비용이 많이 발생하지 않는다면 RDS를 종료하지 않고 유지하려고 했으나, 개인 프로젝트라서 데이터는 그다지 존재하지 않는데

유지 비용이 너무 많이 들어서 프로젝트와 RDS 모두 종료하였다.

프로젝트가 있는 EC2는 다른 프로그램이 동작중이라서 비용은 계속 나갔는데

RDS 유지 비용 때문에 프로젝트를 내려 둔 것이 아깝다고 생각되어서 무료 DB를 사용해서 다시 살려두기로 생각했다.

그래서 찾은 것이 supabase, postgreSQL 다.

 

2. 연결 DB 수정을 위한 초기 작업

프로젝트에서 DB랑 연결하는 방식은 SQL Alchemy로 되어있고, 같은 관계형 DB로 이동하는 것이기 때문에

연결 정보만 수정하면 CRUD 코드는 따로 수정하지 않아도 되지 않나? 싶어서 supabase 계정을 만든 후

비밀번호 설정, 연결을 위한 정보를 받아 SQL ADDRESS를 수정했다.

코드를 수정하기 전에, mysql 이 아닌 postgreSQL 연결을 위해 패키지를 설치했다.

pip install psycopg2
# database.py

from sqlalchemy import create_engine, inspect
from sqlalchemy.orm import sessionmaker, declarative_base
from dotenv import load_dotenv
import os, psycopg2

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
load_dotenv(os.path.join(BASE_DIR, ".env"))

DB_USER = os.environ["sql_db_user"]
DB_PASSWORD = os.environ["sql_db_password"]
DB_HOST = os.environ["sql_db_address"]
DB_PORT = os.environ["sql_db_port"]
DB_NAME = os.environ["sql_db_connect"]

# 기존 연결 정보
# SQL_ADDRESS = f'mysql+pymysql://{os.environ["sql_db_user"]}:{os.environ["sql_db_password"]}@{os.environ["sql_db_address"]}:{os.environ["sql_db_port"]}/{os.environ["sql_db_connect"]}'

# 신규 연결 정보
SQL_ADDRESS = f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'

engine = create_engine(SQL_ADDRESS)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

 

3. 연결 문제 발생

연결 정보를 수정하고 서버를 실행 해봤는데, 시작하자마자 오류가 발생했다.

sqlalchemy.exc.OperationalError (Background on this error at: https://sqlalche.me/e/20/e3q8)

 

Error Messages — SQLAlchemy 2.0 Documentation

Previous: Third Party Integration Issues Next: Changes and Migration Up: Home On this page: Error Messages Connections and Transactions DBAPI Errors SQL Expression Language Object Relational Mapping AsyncIO Exceptions Core Exception Classes ORM Exception C

docs.sqlalchemy.org

내용을 확인해 보니, 연결과 관련된 문제이지만, SQL Alchemy 자체의 문제는 아니라는 것을 보게 되어

GPT에 문의한 결과 크게 두 가지 부분에서 확인할 것을 권유받았다.

1. 연결 정보가 올바른가?

2. SSL 설정을 요구하는 경우, create_engine에 추가 설정이 필요하다

engine = create_engine(SQL_ADDRESS, connect_args={"sslmode": "require"})

 

supabase ssl 요구 여부 확인

supabase 확인 결과 SSL 연결 요청은 하고있지 않아 해당 부분의 문제는 아닌 것으로 보고

연결 정보의 문제가 맞는지 확인하기 위해 연결 정보 중 하나인 user_name 과 password를 고의로 다르게 입력했다.

user_name을 다르게 입력하면 아래와 같이 자세한 문구가 나온다

connection to server at "aws-0-ap-northeast-2.pooler.supabase.com", port 6543 failed: FATAL:  Tenant or user not found

연결 정보를 잘못 입력한 것은 아닌거같다.

 

4. psycopg2 로 직접 연결 검증

그래도 혹시나 uri 정보 중 잘못된 부분이 있지 않을까 싶어서 ORM이 아닌 직접 연결 방식으로 검증해보기로 했다.

conn = psycopg2.connect(
        dbname=DB_NAME,
        user=DB_USER,
        password=DB_PASSWORD,
        host=DB_HOST,
        port=DB_PORT
        )

        cur = conn.cursor()

        cur.execute('SELECT * FROM "Boards"')
        rows = cur.fetchall()

        for row in rows:
            print(row)

        conn.close()

이 때는 문제가 발생하지 않아서 코드 바로 아래에 uri 연결 정보를 입력하고 실행했다.

 

 conn = psycopg2.connect(
        dbname=DB_NAME,
        user=DB_USER,
        password=DB_PASSWORD,
        host=DB_HOST,
        port=DB_PORT
        )

cur = conn.cursor()

cur.execute('SELECT * FROM "Boards"')
rows = cur.fetchall()

for row in rows:
    print(row)

conn.close()
SQL_ADDRESS = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'

print("SQL Address:", SQL_ADDRESS)

# SQLAlchemy 엔진 생성
engine = create_engine(SQL_ADDRESS)
engine.connect()
print("uri connect")

이 때도 uri를 통한 연결 오류를 발생시켰는데, 정확한 원인을 알 수 없음에 점점 화가 나고 있다가

print("SQL Address') 부분을 보고 한 가지가 떠올랐다. '비밀번호에 @가 붙어서 그런가?'

당장 supabase에 들어가서 비밀번호를 @가 붙지 않은 문자열로 바꿔서 시도해 보았다.

 

5. 문제 해결

정말 어이없게도 이 방법이 맞았다. uri 접속 시 DB_HOST 주소를 @ 기준으로 검사한 것으로 보이는데,

비밀번호에 이 정보가 포함되어서 엉뚱한 주소로 접속한 것이고, 이 때문에 연결 오류가 발생한 것이다.

내 경우에는 비밀번호를 바꾸는데 제약이 없어서 바꾸는 것으로 간단히 해결했지만

연결 정보 중에 @ 뿐만 아니라 :, / 등 다른 문자열이 있고, 수정하기 어려운 상황이라면 어떻게 해야하는가?

GPT로 문의해보니 아래와 같이 변경하면 될 것이라고 답변한다.

from urllib.parse import quote_plus
encoded_password = quote_plus(DB_PASSWORD)

 

DB 연결 정보를 바꾸는데 연결 실패로 몇 시간을 할애한 것 치고는 비교적 간단한 해결책이었지만,

오늘도 또 하나 배워간다.