프로그래밍 언어 개발에 관심 있는 사람들의 모임입니다.
|
2008-04-24 16:03:46
|
PHP 웹 프레임워크에 대한 주제로 글이 올라왔는데요. 그래서 ‘좋은 프레임워크의 조건’이라는 진짜 추상적인 주제로 글을 올리려다가, “그냥 좀 더 구체적으로 ‘웹 프레임워크’로 할까?”하고 제목을 썼습니다만, 더 구체적인 편이 좋다 싶어서 “좋은 ORM 프레임워크의 조건은?”이라는 주제를 정하게 됐습니다. (본론도 아닌 것이 이렇게 길다니. ㄷㄷㄷ…) 최근 웹 프레임워크들은 대부분 쓸만한 ORM 프레임워크를 끼고 있습니다. 대표적으로 RoR는 과연 좋은 ORM 프레임워크의 조건에는 어떤 것들이 있을까요? 과연 ORM이 필요할까요? 일단 제 생각을 쓰자면, 좋은 ORM의 조건은 크게 두 가지라고 생각합니다.
전에 Io로 ORM을 작성하려고 한 적이 있습니다. (지금은 잠시 버려뒀습니다만…) Io 특징상 메세지를 객체로 다룰 수 있기 때문에, 아래와 같은 것도 가능하다고 생각했어요.
위 코드에서
사실 어느 정도는 가능할 것 같습니다. 예를 들어 조인.
위와 같이 쓰면 아래와 같은 쿼리를 생성해줄 수 있겠죠.
저 정도로만 쿼리가 나와준다면 사실 SQL을 직접 쓸 일도 사라질 것 같습니다. 무엇보다 ORM을 쓰면서도 실제로는 객체를 보지 못하고 이면의 RDBMS를 생각해야 하는 피곤함(?)이 줄겠죠. 저는 ORM 쓰면서 RDBMS를 자꾸 염두할 바에는 차라리 ORM 없이 SQL만 사용하는 것이 더 속편한 방법이 아니겠느냐 하는 생각도 자꾸 들거든요. 사실 우리가 많이들 사용하는 ORM들은 보통 저런 기대에 부합하지 않죠. RoR의
실제로 쿼리 로그를 보면 이렇습니다.
뒤에 결국 저런 꼴을 보기 싫으면 직접 SQL을 작성하는 수밖에 없습니다. 지연 평가를 잘 이용하면 자주 쓰이는 패턴에 대해서는 똑똑한 쿼리를 생성해줄 수도 있을텐데, 생각보다 그런 것까지 알아서 해주는 ORM이 없더군요. (있으면 알려주세요. 꼭 써볼께요.) 다른 분들은 ORM을 볼 때 어떤 부분을 가장 주의깊게 보시나요? 또, 현존하는 일반적인 ORM들에게 어떤 것들이 부족하다고 생각하시나요? 홍민희 님이 2008-08-22 09:57:57에 고쳤습니다. |
트랙백 주소: http://langdev.net/post/trackback/49
deleted by topbm
예전에 본 기억으로는 PRADO에 ORM 라이브러리가 따로 없었는데, 오늘 찾아보니까 Active Record 패턴을 구현했군요. 그래도 최근에 본 PHP ORM 중에서는 꽤 좋은 것 같습니다.
이렇게 쓰면 N+1번 쿼리 문제를 해결할 수 있을겁니다. (테스트는 안 해봄.)
레일스에서 말하는 생산성이라는 게 주로 이런 종류가 아닐까 하는 생각이 드는군요. -_-;
전 아직 내공이 부족해서 ORM까지 고민해본 적이 없네요. 일단은 생산성에 저해가 되지 않는 선에서 SQL을 살짝 덮어주는 정도면 괜찮을거라고 생각합니다. (MetaBBS도 그런 식이죠. 약간 임시 땜빵 느낌이 나지만) 사실 여러 데이터베이스 엔진을 지원하는 것도 배포용이 아니라면 별로 필요없는 기능인 것 같아요.
물론
find메서드에 지저분한 조건 인수들을 덕지덕지 붙이면 어느 정도 효율적인 쿼리를 가져올 수 있지만, 그렇게 되면 제가 첫 번째 조건으로 걸었던 “RDBMS를 쓰고 있지 않다는 느낌이 얼마나 큰가? 얼마나 일반 객체를 쓰듯이 사용할 수 있는가?”에는 맞지 않겠지요. Ruby에서Array인스턴스를 저렇게 쓰지는 않으니까요.저도 DBMS 중립적이라는 것은 공개 배포를 하지 않는 이상 환상에 불과하다고 생각합니다.
각 언어의 특성을 잘 살리는 것도 중요하지 않을까요? 객체를 다루는 방식이 각 언어마다 다르기 때문에 어느 한 방식이 모든 언어의 ORM에 적용이 되기엔 무리가 있을 것 같습니다.
그리고 blond_222님께서 말씀하신 IDE문제는 ORM 자체에서 더 나아가 IDE의 플러그인이 얼마나 많이 구현되어 있느냐가 더 중요하겠네요. 하이버네이트 같은 경우 MyEclipse라는 플러그인을 사용하면 테이블에서 POJO 자바 파일, 매핑XML 파일, DAO 파일까지 생성을 해 주거든요. PHP도 PDT나 PHPEclipse가 점점 나아지고 있으니 각 ORM에 맞는 플러그인이 나올 수 있겠지요.
네, 제가 말했던 “일반 객체 쓰듯이”라는 것은 물론 각 언어 내에서의 문화를 고려한 이야기였습니다. Io에서는
라고 쓰는 쪽이 자연스러운 객체의 사용일 것이고(왜냐하면 위 코드는 일반적인 Io에서의
List객체 사용하는 것과 정확히 동일한 방법이거든요), Python이라면 아래와 같이 제너레이터 표현식이나 리스트 컴프리헨션(list comprehension)으로도 좋은 쿼리가 나와야겠죠.물론 Python에서 저런 것을 만들기는 거의 불가능합니다. 하지만 Io의 경우 따로 리스트 컴프리헨션 같은 별도의 문법이 있는 것이 아니라, 주로 메서드 체인을 통해
List를 다루므로 비교적 쉽게 달성 가능합니다.map 같은 메서드를 쓰면 평가지연이 힘들어지지 않을까요?
Post.find(:all).map { |p| p.author.name }.slice(0, 10)과 같은 쿼리를SELECT name FROM Posts와 같이 해석을 해주려면… @_@차라리
Post.find(:all).of(:name).slice(0, 10)과 같은 식으로 가져올 필드를 심볼 등을 이용해 넘겨주는 게 낫지 않을까 싶네요.네, 그래서 Python이나 Ruby 같은 언어는 힘들고, Lisp이나 Io 정도 되면 가능합니다.
보다시피 메서드 호출할 때 인자로 쓰이는 표현식이 평가되지 않고 그대로 들어갑니다. (Lisp의 매크로도 이런 식이죠. S-expression 특성상 최소주의적이지만요.)
Lisp에는 리스트가 폼(표현식)들을 저장하는 자료 구조인데, Io의 경우
Message라는 객체 형식이 바로 메세지(표현식)를 담는 자료 구조입니다(Io는 순수 객체 지향 프로그래밍 언어입니다).런타임에 메세지 내용을 분석하고 수정할 수도 있죠. 사실 Python이나 Ruby도 함수, 메서드 객체로부터 AST를 뽑아내면 이런 것이 가능하긴 합니다만, 좀 복잡합니다. 그리고 문법 형식이 다양해서 Io나 Lisp처럼 쉽게 분석할 수도 없겠죠.
Ruby를 보면서 Java가 우습게 느껴졌는데 Io를 보니 이젠 Ruby 그렇군요;
Io도 공부해봐야겠습니다. :D
근데 그렇다면 결론은 좋은 ORM을 만들기 위해서는 언어 자체가 OOP에 충실해야 한다인가요? ^^;
OOP에 충실한 것과 메타프로그래밍 가능성(metaprogrammability?)과는 다른 문제죠~
Io에 관심 있으시면 한국 Io 사용자 모임에도 놀러오세요.
제가 이렇게 “자연스럽게 사용하면서도 알아서 잘 나오는 똑똑한 쿼리”를 얘기하고는 있지만, 사실 제가 작성하고 있는 PHP용 ORM 프레임워크—VLAAH에서 쓰기 위해 만들었던 PostgreSQL 전용의—도 그다지 똑똑한 쿼리를 뽑아주지는 못한답니다;
“자연스럽게 사용하면서도 알아서 잘 나오는 똑똑한 쿼리”가 그렇게 쉽게 나오는 것이라면 이 스레드도 세워지지 않았겠지요. 흐흐 ^^
굳이 일반 객체를 쓰는 것처럼 느껴져야 할 필요는 없다고 생각해서 그렇게 얘기한거에요. 현대적인 RDBMS가 SQL을 쓰는 이상 ORM을 사용하는 코드가 SQL처럼 되어버리는 건 어쩔 수 없잖아요? 예쁘게 보여야 한다면 한번 감싸버리면 그만이죠.
문득 든 생각인데 ORM의 필요성이 언어적 표현으로 DBMS를 다루는 것에 있다면 OOP가 아닌 언어에서는 어떤게 좋을까요?
OOP가 아닌 언어를 잘하시는 분, 계시면 의견 부탁드릴께요.
순수 함수형 프로그래밍 언어에서는 ORM 비슷한 걸 만들더라도
INSERT/UPDATE표현이 불가능하겠죠? 우리가 흔히 말하는 RDBMS라는 것이 결국 상태를 가지고 있고 엔티티(entity) 혹은 터플(tuple)이라고 하는 것도 변할 수 있는(mutable) 값들이니까요.INSERT/UPDATE는 반환되는 결과가 없고 기능의 모든 부분이 부수 효과(side effect)죠;제가 잘 몰라서 그러는데 함수형 언어에서는 부수 효과가 문제가 되나 보네요. Haskell이 함수형 언어이죠? 더 공부해야겠네요. ^^
근데 OOP도 아니고 함수형 언어도 아닌 어정쩡한 우리의 PHP에서는 어떨까요? -ㅅ-;;
side-effect는 함수형 언어에서 문제가 되는 것이 아니라, 관점에 따라서는 모든 언어에서 문제가 됩니다. SICP를 참고하자면 변수 값이 바뀌는 것은 시간에 따라 상태가 변하는 것으로 볼 수 있는데, side-effect를 통해서 상태가 변화할 경우에는 값에 대한 추적이 불가능하고, 때문에 특정한 어떤 순간에 변수가 어떤 값을 가지고 있는지 확인할 방법이 없습니다. side-effect는 시간에 따라 상태를 바꿀 수 있는 손쉽고 빠른 방법이지만, 상태가 변하는 것을 알기 위해서는 결국 디버거의 힘을 빌어야 합니다. 프로그램 검증이나 증명의 관점으로 보자면 side-effect는 말 그대로 “부작용”이 많은 방법입니다.
네, 그래서 저도 절차적인 언어를 사용할 때 최대한 객체를 불변하게 디자인합니다.
루비쪽에선 DataMapper가 엑티브레코드에 비해 좀 더 똑똑합니다. p.author.name를 불러오는 순간 author테이블에서 관련된 레코드들을 읽어오져. 그리고 model쪽에서 특정 필드에 lazy속성을 추가해주면 필요할때만 해당 필드를 읽어들입니다.
속도면에서도 엑티브레코드보다 더 빠르다고 해서 루비쪽 커뮤니티에선 많은 관심을 받고 있다죠.