반디북
데이터 지향 프로그래밍
Ch10
만혁

10. 데이터베이스 작업

관계형 데이터베이스의 데이터에 접근 할 때 데이터 지향 원리를 어떻게 적용하는지에 대한 내용을 설명한다.

이 방법은 NoSQL 에도 적용되는 방법이다.

DOP 에서는 디비에서 얻은 데이터도 애플리케이션의 다른 데이터를 표현할 때와 동일하게

범용 데이터 컬렉션으로 표현한다.

따라서 다음과 같은 특징을 얻는다.

  • 시스템 복잡도 감소
  • 일반성 증가

10.1 데이터베이스에서 데이터를 가져오는 작업

DOP 에서는 언제나 데이터를 범용 데이터 컬렉션으로 표현한다.

이 말은 디비에서 가져오는 데이터나, 메모리의 데이터나 모두 같은 범용 데이터 컬렉션을 사용한다는 뜻이다.

JDBC 결과 집합을 맵의 리스트로 변환 할 때, 각 값은 Object로 취급된다

값에 접근하기 위해 값을 매번 캐스팅해줘야한다.

정확히는 값의 구체적인 자료형이 중요할 때는 JSON 으로 직렬화 하게 된다

이 방식은 지연 바인딩(late binding) 이라 한다.

10.3 단순한 데이터 조작

디비의 컬럼 이름을 애플리케이션에 적합한 이름으로 변경해줘야 할 때가 있다.

디비의 user_name 을 앱에서 userName 으로 변경해줘야하는 상황

SELECT
    title AS bookTitle,
    isbn,
    publication_year AS publicationYear
FROM
    books
WHERE 
    title LIKE '%habit%';

이렇게 해도 해결은 되지만 애플리케이션의 작명 규칙을 위해 쿼리문을 수정하는것은 이상하게 느껴진다

따라서 애플리케이션에서 필드 이름을 다루는게 더 합리적이다.

(근데 JPA 에선 기능 제공해주긴함ㅋ)

function renameBookInfoKeys(bookinfo) {
    return {
        "bookTitle": _.get(bookIrrFo, "title"),
        "isbn": _.get(bookinfo, "isbn"),
        "publicationYear": _.get(bookinfo, "publication_year")
    };
}

위와 같이 특정 키를 변경할 수 있게 된다

하지만 좀더 범용적인 함수를 만들자면 이와 같다.

function renameKeys(map, keyMap) {
    return _.reduce(keyMap,
        function(res, newKey, oldKey) {
            var value = _.get(map, oldKey);
            var resWithNewKey = _.set(res, newKey, value);
            var resWithoutOldKey = _.omit(resWithNewKey, oldKey);
        return resWithoutOldKey;
    },
    map);
}
function renameResultKeys(results, keyMap) {
    return _.map(results, function(result) {
        return renameKeys(result, keyMap);
});
}
 
 
renameResultKeys(bookResults, {
    "title": "bookTitle",
    "publication_year": "publicationYear"
});

10.4 복잡한 데이터 조작

1, 7 Habits of Highly Effective People, 978-1982137274, Sean Covey
2, 7 Habits of Highly Effective People, 978-1982137274, Stephen Cove
3, The Power of Habit, 978-0812981605, Charles Duhigg

디비 쿼리를 질의하다보면 종종 위와 같이 중복되는 결과값을 받을때가 있다.

위 결과값은 작성자의 이름이 두개기 때문에 동일한 책이 두번 나오는것인데

이를 해결하기 위해서는 주로 쿼리를 수정하게 된다 (서브 쿼리를 사용하거나 어쩌거나...)

쿼리를 수정하게되면 로직이 쿼리에 들어가게되어 점점 더 복잡한 쿼리를 만들고 있는 자신을 보게 된다

DOP 에서는 이 상황에서 어떤식으로 코드를 활용해 처리하는지 보여준다

방법은 간단하다.

  1. isbn 으로 groupBy 를 사용해 해당 결과들을 맵으로 만든다
  2. author names 취합
// isbn 으로 그룹화
_.groupBy(rawByIsbn, "isbn")
 
// result
// {
//     "978-1982137274": [],
//     "978-0812981605": [],
// }
// 저자 이름 취합
var authorNames = _.map(rows7Habits, "author_name");
var firstRow = _.nth(rows7Habits, 0);
var booklnfoWithAuthorNames = _.set(firstRow, "authorNames", authorNames);
_.omit(booklnfoWithAuthorNames, "authorjiame");
// 임의 필드 취합
function aggregateField(rows, fieldName, aggregateFieldName) {
    var aggregatedValues = _.map(rows, fieldName);
    var firstRow = _.nth(rows, 0);
    var firstRowWithAggregatedValues = _.set(firstRow,
        aggregateFieldName,
        aggregatedValues);
    return omit(firstRowWithAggregatedValues, fieldName);
}
aggregateField(rows7Habits, "author_name", "authorNames")
// 행의 리스트에서 특정 필드 취합
function aggregateFields(rows, idFieldName, fieldName, aggregateFieldName) {
    var groupedRows = values(_.groupBy(rowsidFieldName));
        return _.map(groupedRows, function(groupedRows) {
            return aggregateField(groupedRows^ fieldName, aggregateFieldName);
    });
}
 
aggregateFields(sqlRows, "isbn", "author_name", "authorNames");

요약

디비에 데이터를 질의하다 보면 자주 겪게되는 상황이다

레거시 코드에서는 이런 경우에 쿼리를 수정해 완성된 결과를 도출하도록 되어있었다.

과거의 나도 그게 제일 깔끔한 방법이라고 생각했었는데, 쿼리에 로직이 붙기 시작하더니

쿼리가 기본 백줄이 넘어가기 시작하는 상황을 마주하고 나서는 쿼리를 최대한 단순하게 작성는 습관이 들었다.

결국엔 책에서 소개한 방법대로 데이터를 조회한 뒤 코드단에서 내가 원하는 데이터 포맷으로 가공하는

로직들을 많이 추가했었는데 10장의 내용처럼 모듈화하면 더 좋았을것 같다.

우석