mongoDB + Spring Data JPA


지난 시간 간단하게 2개의 Collection으로 구성된 mongoDB의 Document 구조를 설계해 보았다.

하지만 많은 시스템이 그렇듯 한 번에 완결이 되는 그런 개발은 없다. 아무리 작은 개인 프로젝트라도…

물론 가능한한 완벽한 설계를 한다면 그 변경의 폭은 줄어들 수 있겠지만 아마도 계획되지 않은 진행을

완전히 막을 수는 없을 것이다.


오늘은 지난 시간에 이어 Collection 중에 Elements Collection을 조금 변경해볼 것이다.


1. 기능 목표


To-Do 기능의 추가 : 가능한한 단순한 시스템을 만들고자 했으나 목표를 정하고 그 목표를 향해

가는 시스템에서 간단하게나마 계획을 하는 부분을 제외하는 것은 무리인 듯했다. 결국 To-Do

기능을 추가하고 그와 관련하여 예정된 작업과 완료된 작업 간의 상태 이동이나 예정된 작업에서

예정일을 변경하는 기능 그리고 그와 관련된 화면 구현 등의 작업이 진행 될 것이다.


2. 기술 목표


이번까지의 목표는 mongoDB에 대해 조금 더 알아가는 것이다. 이번에는 Document 구조를

바꿔가면서 어떤 구조가 back end나 front end에서 데이터를 다루기가 더 쉬운지를 확인해 볼

것이다. 하지만 mongoDB에 대한 좀 더 심화된 내용은 없을 것이다. 아울러 데이터 처리를 위해

Spring Data JPA의 query method를 사용하는 법을 간단하게 알아볼 것이다.


3. 기술 적용 내용


mongoDB


우선 지난 포스팅에서 설계를 마쳤던 2개의 Collection 중 Profile Collection은 이름을 Profile

AndGoal로 바꾸었고 그 구조적으로는 기본 계정을 제외한 몇개의 항목을 profile이라는 이름의

Sub Document로 만들기로 했다. 우선 지난 시간 만들었던 ProfileAndGoal Collection의

Document 구조를 한 번 보자


ProfileAndGoal Collection


{

    "_id" : ObjectId("584ff54a4fe35068dc44bab4"),

    "_class" : "com.mazdah.tillsixty.account.domain.Profile",

    "name" : "우형준",

    "userId" : "mazdah",

    "password" : "w00hj8928",

    "email" : "woohj70@gmail.com",

    "facebook" : "100000967897658",

    "twitter" : "@mazdah70",

    "link" : "http://mazdah.tistory.com",

    "introduction" : "평범한 아들. 평범한 남자. 평범한 가장. 평범한 개발자. 평범한 서민. 하지만 그 '평범'이 

    너무도 싫은 한 사람",

    "goalList" : [ 

        {

            "goalId" : "584ff54a4fe35068dc44bab4_1",

            "goalTitle" : "Project Master 개발",

            "startDate" : "2016-12-13",

            "endDate" : "2017-12-31",

            "goalDescription" : "관리자와 개발자 모두 쉽게 사용할 수 있는 프로젝트 관리 툴 개발\n

  프로젝트 진행 과정을 쉽게 시각화 문서화하여 진행 상태를 직관적으로\n

  확인할 수 있도록 개발",

           "goalStatus" : "O",

           "createDate" : "2016-12-13"

        }

    ]

}


위 Document 중 email, facebook, twitter, link, imgPath, introduction 등 6개의 항목을 

Sub Document로 만들기로 하여 다음과 같은 구조로 변경하였다.


{

    "_id" : ObjectId("587e18514216afd4103453c2"),

    "_class" : "com.mazdah.tillsixty.account.domain.User",

    "name" : "우형준",

    "userId" : "mazdah",

    "password" : "w00hj8928",

    "profile" : {

        "facebook" : "100000967897658",

        "twitter" : "@mazdah",

        "link" : "http://mazdah.tistory.com",

        "introduction" : "평범한 아들. \n평범한 남자. \n평범한 가장. \n평범한 개발자. \N

    평범한 서민. \n하지만 그 '평범'이 너무도 싫은 한 사람"

    },

    "goalList" : [ 

        {

            "goalId" : "587e18514216afd4103453c2_1",

            "goalTitle" : "AI 전문가 되기",

            "startDate" : "2017-01-17",

            "endDate" : "2017-12-31",

            "goalDescription" : "TensorFolw를 시작으로 머신 러닝과 딥 러닝을 학습하여 인공 지능 

  분야의\n전문가가 될 수 있도록 함",

            "goalStatus" : "O",

            "createDate" : "2017-01-17"

        }

    ]

}


사실 사용자 계정과 Profile 정보는 명백하게 1:1의 관계이고 크게 변동이 있을만한 정보들이

아니기 때문에 굳이 Sub Document로 만들이 않아도 되었을 것이다. 하지만 분명 기본 

계정 정보와는 성격이 다른 정보들이고 추후 항목이 추가될 수도 있으며 RDBMS로의 전환을

고려한다면 일단 구조적으로도 분리시켜 놓는 것이 좋을 것이라는 판단이다.


정작 많은 고려를 해야 하는 것은 Elements쪽이었다. To-Do 기능을 추가함에 따라 하나의

Element가 상태가 변할 수도 있고 그에 따라 몇몇 정보도 같이 변화하기 때문에 이에 맞는

구조를 찾아야 하는 것이다. 그리고 그 과정에서 mongoDB의 제약 사항인 join과 transa-

ction에 대한 고려를 함께 해야 한다. 이러한 고려사항 때문에 2차례의 수정을 거쳐 마지막

구조를 만들었는데 아무래도 한 번 더 수정을 해야 할 것 같다.


처음에는 RDBMS 모델링을 하던 습관 때문인지 Element의 상태를 Sub Document로

만들었다. 구조는 다음과 같다


Elements Collection


{

    "_id" : ObjectId("58515bb94fe35068dc44bac3"),

    "_class" : "com.mazdah.tillsixty.goal.domain.Elements",

    "userId" : "584ff54a4fe35068dc44bab4",

    "goalId" : "584ff54a4fe35068dc44bab4_1",

    "elementType" : "A",

    "title" : "메인화면 구현",

    "description" : "Parallax 스타일의 메인화면 구현",

    "createDate" : "2016-12-14”,

“statusList” : [

{

“status” : “예정”,

“dueDate” : “2017-01-31”

},

{

“status” : “지연”,

“dueDate” : “2017-02-05”

},

{

“status” : “완료”,

“dueDate” : “2017-01-31”

}

]

}


이 경우 statusList를 통한 조회가 번거로워지기도 하고 또 상태 중 완료에 대한

처리가 모호해진다. 상태는 완료인데 날짜 필드를 dueDate라고 하기에도 뭔가

매칭이 안되고 그렇다고 완료의 경우에는 endDate라는 필드명을 쓰거나 아니면

endDate라는 필드를 추가하는 것도 데이터 구조의 일관성이 떨어지는 것 같다.


그래서 다시 다음과 같이 바꾸었다.


{

    "_id" : ObjectId("587e1e2b4216afd4103453c8"),

    "_class" : "com.mazdah.tillsixty.elements.domain.Elements",

    "userId" : "587e18514216afd4103453c2",

    "goalId" : "587e18514216afd4103453c2_1",

    "elementId" : NumberLong(1484660267204),

    "elementType" : "A",

    "title" : "Action ToDo 등록 테스트 2",

    "description" : "공공 데이터 중 선형회귀 분석으로 분석 가능한 데이터 구하기",

    "createDate" : "2017-01-17",

    "dueDateList" : [ 

        "2017-02-04", 

        "2017-01-31", 

        "2017-02-10"

    ],

    "endDate" : "2017-01-19",

    "status" : "완료"

}

간단해진 것은 좋으니 이 구조는 상태 이력을 파악하기가 쉽지 않다…ㅠ.ㅠ 즉, Timeline

처리가 어려워진다는 말이다. 결국은 상태가 바뀔 때마다 새로운 Document를 생성하고

같은 Element에 대해 상태별로 생성된 Document들은 group Id로 묶는 것으로 다시 

구조를 바꾸어야 할 것 같다.


중요한 것은 이렇게 Document를 바꿔나가는 동안 mongoDB쪽에는 한 일이 없다는

것이다. 이전 구조의 데이터를 삭제한 것 외에는 다른 어떤 작업도 필요 없었다. 바로

schemeless의 위력을 실감한 것이다. 하지만 바뀐 구조에 대한 프로그램 내에서의 

처리를 고려한다면 전체적인 의미에서의 작업이 단순해진 것은 아니다. 다만 DB쪽

작업이 개발쪽으로 옮겨왔을 뿐…ㅠ.ㅠ 그래도 역시 mongoDB 설계의 유연함은

상당히 만족스럽다.


Spring DATA REST


Document 구조가 바뀜에 따라 가장 크게 영향을 받는 것이 바로 REST API의 변경이

아닐까 싶다. 하지만 아직은 너무나 단순한 DB를 구현하고 있기 때문에 가장 기본적인

Query Method만으로도 충분히 처리가 가능하였다. 우선 Spring DATA REST에서

제공하는 keyword를 method 이름에 사용함으로써 웬만한 query는 다 처리가 되었다.

예를 들어 아래와 같은 REST 호출을 보자

“/rest/elements/search/findByUserIdAndGoalIdAndStatusInOrderByCreateDateAsc?” +

“UserId=” + userId + “&goalId=” + goalId + “&status=예정&status=연기


엄청 긴 API가 보인다. “findByUserIdAndGoalIdAndStatusInOrderByCreateDateAsc”

이름 만으로도 아마 충분히 짐작을 할 것이다. 이 API를 SQL Query로 바꾸면 다음과 같다.


select * from [TABLE]

where userId = [userId]

and goalId = [goalId]

and status in ([status1], [status2])

order by createDate asc


특별히 테이블 간의 join이 필요 없다면 이렇게 method이름만으로 대부분 처리가 가능하다.

mongoDB의 경우 join이 없다고 봐도 무방하므로 사용하기가 아주 편해진다.

Query method에 사용 가능한 keyword의 목록은 아래 문서에 자세히 정리되어있다.

문서에서 “Supported keywords inside method names”로 검색을 하면 keywords

테아블에서 확인 가능하다.


4. 참조 (site 또는 서적)


Spring Data JPA : http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#repositories.single-repository-behaviour 


5. 확인 사항 (의문 사항)


지난 시간에 목표했던 내용을 아직 확인 못했다. mongoDB에서의 트랜잭션 처리와 관련하여 

$isolated라는 연산자 확인이 필요하다.


6. 평가


애초에 DB에 대한 지식이 부족한 상태이다보니 모델링 작업은 쉽지 않았다. 나름대로 여기저기서

지식을 빌어다가 모델링을 해보기는 하였지만 아직도 완성된 모습은 아니다. 이렇게 간단한 시스템

개발에도 이럴진대…ㅠ.ㅠ 마음을 비우고 공부에 전념해야겠다.


일단 Till60의 기본적인 기능은 어느정도 구현되었으니 차주부터는 js쪽 소스들에 대해 React를

적용하는 작업을 본격적으로 시작해볼 예정이다. 조급해하지 말고 한발 한발 나가보자!

블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요