오늘 배운 것

어제 Intellij에서 xml 파일을 작성해 주다가 xml에서 참조하는 dtd 파일의 링크가 유효하지 않아서 오류가 발생했었다. 그래서 우회하는 방법으로 여러 커맨드를 사용해 보니, 꼭 reveng.xml 파일을 사용하지 않아도 장고 모델을 스프링 엔티티로 바꿀 수 있는 것처럼 보였다. 

 

요약하자면 다음과 같다. 

1. 장고의 inspectdb 명령어를 이용해서 스키마 안의 테이블들을 전부 장고 모델로 바꾼다. 

2. hibernate에서 장고에서 사용하던 DB와 연결한다. 

3. hibernate tools를 통해 DB를 참고하여 스프링 엔티티 클래스를 만든다. 

4. 장고 모델과 동일하게, 1번을 참고하면서 4번의 스프링 엔티티 클래스를 수정한다. 

 

처음에는 1번 과정이 왜 필요한가 싶었는데, 혹시나 4번 과정에서 생성된 스프링 엔티티 클래스가 잘 생성된 것인지를 직접 비교하면서 판단하기 위해서는 필요할 것 같았다. 

 

1번 과정의 경우, 장고의 inspectdb라는 명령어를 사용하면 장고가 현재 사용하고 있는 DB(스키마)에 있는 모든 테이블들을 장고의 모델 클래스로 옮길 수 있다. 공식문서에도 친절하게 설명이 나와있었다.


그런데 inspectdb 명령어는 원래는 반대로 기존에 다른 프로젝트에서 사용하고 있는 데이터베이스를 장고 WAS에서도 참조하고 싶을 때 사용하는 것으로 보였다. inspectdb 명령어로 생성된 모델은 managed 속성값이 False로, 해당 모델의 CRUD(인스턴스가 아니라, 해당 모델 클래스에 대한 CRUD)가 장고 ORM에서 관리되지 않는다고 한다. 

 

그 외에도 데이터베이스별로 컬럼 타입 등이 조금씩 다를 수 있기 때문에 만약 데이터베이스의 어떤 컬럼 타입을 장고에서 사용하는 컬럼 타입으로 바꿀 수 없는 경우 기본으로 TextField를 할당한다고 되어있었다. 또한 데이터베이스에서 필드 이름으로 파이썬의 예약어(reserved word)를 사용한 경우 뒤에 '_field'를 추가로 붙여준다고 한다. 

 

inspectdb 커맨드의 소스 코드를 좀 보려고 했는데 양이 너무 길어서 다 보진 못했다. 'django.core.management.commands' 안에 있는 파일들이 모두 각각의 장고에서 제공하는 기본 커맨드이고, 모든 파일에서는 전부 BaseCommand라는 기본 클래스를 상속받는다. 

 

BaseCommand는 어떤 클래스도 상속받지 않은 기본 클래스이며, BaseCommand를 상속받은 여러 AppCommand, LabelCommand 등도 있다. AppCommand, LabelCommand 클래스들은 BaseCommand처럼 base.py에 정의된 걸 보니 얘네들을 상속받는 또 다른 커맨드 클래스들이 있는 것 같다. 

 

BaseCommand를 상속받아서 'django-admin'이나 'python manage.py' 뒤에 사용될 수 있는 커맨드를 만들고 싶다면 이 BaseCommand를 상속받으면 된다. 그리고 'handle'이라는 메소드를 오버라이딩해서 해당 커맨드가 어떤 역할을 할지를 정의해주면 된다. 

 

원리도 주석으로 상세히 설명되어 있었는데, 여러 메소드가 연쇄적으로 호출되는 것 같았다. 우선 처음에는 'django-admin'이나 'python manage.py' 명령어를 호출하면 뒤에 붙은 커맨드 클래스의 'run_from_argv' 메소드가 호출된다. 그리고 해당 메소드는 'create_parser' 메소드를 호출해서 해당 커맨드 뒤에 인자로 붙은 것이 있다면 'ArgumentParser' 클래스를 통해 파싱(parsing)을 한다. 

manage.py
django.core.management.__init__.py
django.core.management.__init__.py

그리고 그 파싱된 인자들을 가지고 'execute' 메소드를 실행한다. 'execute' 메소드에서는 'handle' 메소드를 호출하여 메소드의 핵심 로직을 실행시킨다. 만약 두 메소드 안에서 오류가 날 경우 'stderr'를 포함한 오류 메시지가 포함된다고 한다. 

django.core.management.base.py
django.core.management.base.py

 

아무튼 그래서 inspectdb도 당연히 handle 메소드가 있었다. handle 메소드 안에서는 handle_inspection이라는 또 다른 메소드를 호출하고 있었다. 

django.core.management.commands.inspectdb

handle_inspection 메소드는 너무 길어서 자세히 보진 못했다. 그래도 맨 처음에 DB에 커넥션을 맺고, 해당 커넥션을 통해 DB 스키마의 테이블들, 필드 이름들을 전부 가져오는 것으로 보였다. 아무튼 해당 작업을 통해서 DB 스키마에 있는 테이블들을 장고 모델로 바꿔준다는 것을 대략적으로나마 알 수 있었다. 

DB에 커넥션 맺기
DB 스키마의 테이블들 가져와서 for문으로 처리하기


이제 2번 작업으로 넘어가자. 그런데 아까처럼 .dtd 파일에서 오류가 나는 상황이다. 관련 공식문서를 찾아보았지만 현재 Reverse Engineering 기능은 유료인 Ultimate 버전에서만 제공되는 것 같았다. 알고보니 Intellij 무료 버전에는 내장된 데이터베이스 브라우저가 없기 때문에 이런 기능이 기본적으로 제공되지 않는다고 한다. 

 

사실 테이블 몇 개 옮기는 게 그렇게 번거로운 작업은 아닌데, 문제는 장고 모델이 변경될 때마다 계속 그에 맞춰 스프링 엔티티를 변경해 줘야 한다는 거였다. 그래서 JPABuddy라는 툴을 사용하는 방법도 있다고 한다. 무료 버전에서는 기능이 제한적인데, 최대한 무료 기능으로 한번 해결해 보자. 

 

궁금한 점

1. inspectdb 명령어로 생성된 장고 모델 중에서는 allauth나 simplejwt 같은 외부 라이브러리를 사용하면서 생성된 것들도 있는데, 이것들도 스프링 엔티티로 생성해야 할지 궁금하다. 왜냐하면 스프링에는 장고의 allauth, simplejwt와 똑같은 기능을 하는 라이브러리가 없을 수도 있고, 있더라도 그 라이브러리를 직접 사용해서 쓰는 게 더 맞기 때문이다. 이 부분은 잘 모르겠지만 일단은 이렇게 자체적으로 생성되지 않고 외부 라이브러리를 통해 자동적으로 생성된 모델들은 일단은 스프링 엔티티로 옮기지 말고 진행해야겠다. 

2. 'handle_inspection' 메소드에서 yield 문법이 나왔는데 여기에 대해서 잘 모르는 것 같다. 다음에 더 알아보자. 

 

+ Recent posts