neo4j 리팩토링 튜토리얼

Soogyung Gwon·2026년 3월 2일

구름을잡아라

목록 보기
27/76

https://neo4j.com/docs/getting-started/data-modeling/tutorial-refactoring/

리팩토링이란?

데이터 모델과 그래프를 바꾸는 과정

그 이유로는:

  • 그래프 모델이 모든 유즈 케이스를 커버하지 않음
  • 새로운 유즈 케이스가 발생
  • Cypher가 유즈 케이스를 고려할 때 최적의 성능을 보이지 않음, 특히 그래프 규모가 커질 때

실습을 위한 그래프 만들기 - 영화 그래프

CREATE (Apollo13:Movie {title: 'Apollo 13', tmdbID: 568, released: '1995-06-30', imdbRating: 7.6, genres: ['Drama', 'Adventure', 'IMAX']})
CREATE (TomH:Person {name: 'Tom Hanks', tmdbID: 31, born: '1956-07-09'})
CREATE (MegR:Person {name: 'Meg Ryan', tmdbID: 5344, born: '1961-11-19'})
CREATE (DannyD:Person {name: 'Danny DeVito', tmdbID: 518, born: '1944-11-17'})
CREATE (JackN:Person {name: 'Jack Nicholson', tmdbID: 514, born: '1937-04-22'})
CREATE (SleeplessInSeattle:Movie {title: 'Sleepless in Seattle', tmdbID: 858, released: '1993-06-25', imdbRating: 6.8, genres: ['Comedy', 'Drama', 'Romance']})
CREATE (Hoffa:Movie {title: 'Hoffa', tmdbID: 10410, released: '1992-12-25', imdbRating: 6.6, genres: ['Crime', 'Drama']})

MERGE (TomH)-[:ACTED_IN {roles:'Jim Lovell'}]->(Apollo13)
MERGE (TomH)-[:ACTED_IN {roles:'Sam Baldwin'}]->(SleeplessInSeattle)
MERGE (MegR)-[:ACTED_IN {roles:'Annie Reed'}]->(SleeplessInSeattle)
MERGE (DannyD)-[:DIRECTED]->(Hoffa)
MERGE (DannyD)-[:ACTED_IN {roles:'Robert "Bobby" Ciaro'}]->(Hoffa)
MERGE (JackN)-[:ACTED_IN {roles:'Hoffa'}]->(Hoffa)

CREATE (Sandy:User {name: 'Sandy Jones', userID: 1})
CREATE (Clinton:User {name: 'Clinton Spencer', userID: 2})

MERGE (Sandy)-[:RATED {rating:5}]->(Apollo13)
MERGE (Sandy)-[:RATED {rating:4}]->(SleeplessInSeattle)
MERGE (Clinton)-[:RATED {rating:3}]->(Apollo13)
MERGE (Clinton)-[:RATED {rating:3}]->(SleeplessInSeattle)
MERGE (Clinton)-[:RATED {rating:3}]->(Hoffa)

새로운 데이터 추가

MATCH (Apollo13:Movie {title:'Apollo 13'})
MATCH (SleeplessInSeattle:Movie {title:'Sleepless in Seattle'})
MATCH (Hoffa:Movie {title:'Hoffa'})
SET Apollo13.languages = ['English']
SET SleeplessInSeattle.languages = ['English']
SET Hoffa.languages = ['English', 'Italian', 'Latin']

언어에 따른 영화 조회

MATCH (m:Movie)
WHERE 'English' IN m.languages
RETURN m.title

위 쿼리의 문제점

위의 쿼리는 모든 영화 노드를 체크하여 언어 프로퍼티가 영어인지 확인한다. 이는 틀린 것은 아니지만, 두 가지 이슈가 있을 수 있다:

  • 쿼리를 실행하기 위해서 모든 영화 노드를 방문해야 한다. - 그래프의 규모가 커짐에 따라 모델의 구조 때문에 이러한 쿼리의 성능은 떨어지게 된다.
  • 같은 프로퍼티, 언어의 값이 많은 영화 노드에 동일하게 존재한다. - 만일 많은 노드들이 같은 프로퍼티 값을 갖는다면 이는 이 프로퍼티 값이 새로운 엔티티(노드 혹은 관계)가 될 수 있다는 증거가 된다.

해결방법

언어 프로퍼티를 리팩토링 하고 영화 노드에 새로운 관계로 연결하는 것으로 해결할 수 있다.

리팩토링 과정

중복 데이터 제거

MATCH (m:Movie)
WITH m, m.languages AS languages
UNWIND languages AS language
MERGE (l:Language {name: language})
MERGE (m)-[:IN_LANGUAGE]->(l)
REMOVE m.languages

실행하면 아래와 같이 됨:

복잡한 데이터 다루기

만약 각 영화의 프로듀서에 대한 새로운 유즈 케이스가 필요하게 되었다고 할 때, 프로듀서의 실제 주소를 포함한 프로듀서에 대한 데이터는 복잡한 데이터라고 볼 수있다.

이러한 정보를 Productioncompany라는 노드에 주소 프로퍼티와 함께 그래프에 추가할 수 있다.

CREATE (p:ProductionCompany {name:'Imagine Entertainment', country:'US', postalCode:90212, state:'CA', city:'Beverly Hills', address1:'10351 Santa Monica Blvd'})
MERGE (Apollo13:Movie {title:'Apollo 13'})
CREATE (p)-[:PRODUCED]->(Apollo13)
CREATE (jerseyFilms:ProductionCompany {name:'Jersey Films', country:'US', postalCode:90049, state:'CA', city:'Los Angeles', address1:'10351 Santa Monica Blvd'})
MERGE (hoffa:Movie {title:'Hoffa'})
CREATE (jerseyFilms)-[:PRODUCED]->(hoffa)

그러나 이렇게 데이터를 저장하는 것은 몇가지 부분에서 좋지 않을 수 있다:

  • 중복 데이터: 혹여 있을 수도 있는 같은 이름의 프로덕션 이름이나, 장소 등
  • 불필요한 정보 검색 (Over-fetching): 예를 들어 캘리포니아에 있는 회사를 찾으려 한다면, ProductionCompany 노드의 모든 프로퍼티를 체크해서 California 를 찾아내야 한다.
  • 대안으로 index를 만드는 방법도 있다.

데이터 모델링에 있어서의 목표는 쿼리로 체크해야 하는 그래프의 사이즈를 줄이는 데 있다.

위의 예에서는 두 개의 회사가 캘리포니아에 있으므로 주를 다른 노드로서 추가할 수 있을 것이다.

또 다른 예로서 1995년에 영화에 출연한 배우를 빈번히 조회해야 한다면 ACTED_IN_1995라는 지정된 관계를 만들게 되면 이 특정 관계를 찾아 조회하면 되므로 불필요한 체크를 피할 수 있어 쿼리의 효율을 높일 수 있다.

리팩토링 후 테스트

리팩토링이 끝난 후에는 모든 유즈 케이즈의 쿼리를 체크하여 모두 원활하게 작동하는지 체크하고, 업데이트 되야할 쿼리문이 있다면 업데이트를 해준다.

만약 리팩토링 후 쿼리문의 성능이 올라갔는지 보기 위해서는 쿼리문을 실행했을 때 검색되는 요소들의 수를 체크해 볼 수 있다.

profile
오랜시간 망설였던 코딩을 다시 해보려고 노력하고 있는 사람

0개의 댓글