가중치 그래프와 방향 그래프 — 현실 세계를 그래프로 모델링하기
현실 세계의 관계는 단순한 "연결/비연결"이 아닙니다. 방향이 있고, 비용이 다릅니다. 이런 현실을 그래프로 어떻게 표현할까요?
방향 그래프 (Directed Graph)
방향 그래프는 간선에 방향 이 있는 그래프입니다. A에서 B로의 간선이 있다고 해서 B에서 A로의 간선이 있는 것은 아닙니다.
방향 그래프:
A → B
↑ ↓
D ← C
무방향 그래프:
A — B
| |
D — C
인접 리스트로 표현
public class DirectedGraph {
private List<List<Integer>> adjList;
private int vertices;
public DirectedGraph(int vertices) {
this.vertices = vertices;
adjList = new ArrayList<>();
for (int i = 0; i < vertices; i++) {
adjList.add(new ArrayList<>());
}
}
// 방향 그래프: u → v만 추가
public void addEdge(int u, int v) {
adjList.get(u).add(v);
// adjList.get(v).add(u); ← 이 줄이 없음!
}
// 진입 차수(in-degree) 계산
public int[] calculateInDegree() {
int[] inDegree = new int[vertices];
for (int u = 0; u < vertices; u++) {
for (int v : adjList.get(u)) {
inDegree[v]++;
}
}
return inDegree;
}
}
인접 행렬로 표현
// 방향 그래프의 인접 행렬은 비대칭
// A=0, B=1, C=2, D=3
// A→B, B→C, C→D, D→A
int[][] matrix = {
{0, 1, 0, 0}, // A → B
{0, 0, 1, 0}, // B → C
{0, 0, 0, 1}, // C → D
{1, 0, 0, 0} // D → A
};
가중치 그래프 (Weighted Graph)
간선에 비용, 거리, 시간 등의 가중치가 붙은 그래프입니다.
5
A ───→ B
│ │
3│ │2
↓ ↓
D ←──── C
4
인접 리스트로 표현
public class WeightedGraph {
static class Edge {
int to;
int weight;
Edge(int to, int weight) {
this.to = to;
this.weight = weight;
}
}
private List<List<Edge>> adjList;
private int vertices;
public WeightedGraph(int vertices) {
this.vertices = vertices;
adjList = new ArrayList<>();
for (int i = 0; i < vertices; i++) {
adjList.add(new ArrayList<>());
}
}
public void addEdge(int u, int v, int weight) {
adjList.get(u).add(new Edge(v, weight));
}
public List<Edge> neighbors(int u) {
return adjList.get(u);
}
}
인접 행렬로 표현
// 가중치 인접 행렬: 0 대신 가중치 값, 연결 없으면 무한대
int INF = Integer.MAX_VALUE;
int[][] matrix = {
{0, 5, INF, 3}, // A: B(5), D(3)
{INF, 0, 2, INF}, // B: C(2)
{INF, INF, 0, 4}, // C: D(4)
{INF, INF, INF, 0} // D: 없음
};
현실 세계를 그래프로 모델링
1. SNS 팔로우 관계 — 방향 무가중치 그래프
// A가 B를 팔로우 → 방향 간선 A → B
DirectedGraph sns = new DirectedGraph(userCount);
sns.addEdge(alice, bob); // Alice → Bob (팔로우)
// Bob은 Alice를 팔로우하지 않을 수 있음
// 인기 사용자 = 진입 차수(follower 수)가 높은 정점
int[] inDegree = sns.calculateInDegree();
// inDegree[bob] = Bob의 팔로워 수
실제 Twitter/Instagram은 이 구조를 기반으로 합니다.
2. 도로 네트워크 — 방향 가중치 그래프
// 교차로 = 정점, 도로 = 간선, 거리/시간 = 가중치
WeightedGraph roadNetwork = new WeightedGraph(intersectionCount);
// 일방통행도 표현 가능
roadNetwork.addEdge(gangnam, yeoksam, 5); // 강남→역삼: 5분
roadNetwork.addEdge(yeoksam, gangnam, 7); // 역삼→강남: 7분 (역방향 다를 수 있음)
// 네비게이션 = Dijkstra 알고리즘으로 최단 경로 계산
int[] shortestTime = dijkstra(roadNetwork, startIntersection);
3. API 호출 그래프 — 방향 가중치 그래프
마이크로서비스 아키텍처에서 서비스 간 호출 관계를 그래프로 표현할 수 있습니다.
// 서비스 = 정점, API 호출 = 간선, 지연 시간 = 가중치
WeightedGraph serviceGraph = new WeightedGraph(serviceCount);
serviceGraph.addEdge(gateway, userService, 10); // 게이트웨이 → 유저 서비스: 10ms
serviceGraph.addEdge(userService, authService, 5); // 유저 → 인증: 5ms
serviceGraph.addEdge(gateway, orderService, 15); // 게이트웨이 → 주문: 15ms
serviceGraph.addEdge(orderService, paymentService, 20); // 주문 → 결제: 20ms
이 그래프로 할 수 있는 분석:
- ** 크리티컬 패스 **: 가장 긴 경로 → 전체 응답 시간의 병목
- ** 순환 의존성 **: 사이클 감지 → 아키텍처 문제
- ** 장애 전파 **: 특정 서비스 장애 시 영향받는 서비스 범위
4. 웹 페이지 — 방향 그래프
PageRank:
- 웹 페이지 = 정점
- 하이퍼링크 = 방향 간선
- Google의 PageRank는 이 방향 그래프에서 각 페이지의 "중요도"를 계산
5. 작업 의존성 — DAG (방향 비순환 그래프)
// 빌드 시스템: 작업 간 의존성
// compile → test → package → deploy
WeightedGraph buildPipeline = new WeightedGraph(4);
buildPipeline.addEdge(compile, test, 30); // 컴파일(30초) 후 테스트
buildPipeline.addEdge(test, pack, 60); // 테스트(60초) 후 패키징
buildPipeline.addEdge(pack, deploy, 10); // 패키징(10초) 후 배포
음수 가중치의 문제
가중치가 음수인 경우 특별한 주의가 필요합니다.
2
A ───→ B
│ │
5│ │-10
↓ ↓
D ←──── C
3
Dijkstra는 음수 가중치를 처리하지 못합니다
Dijkstra는 "이미 확정한 최단 거리는 변하지 않는다"는 가정에 기반합니다. 음수 가중치가 있으면 이 가정이 깨집니다.
Bellman-Ford 알고리즘
public int[] bellmanFord(int vertices, List<int[]> edges, int start) {
int[] dist = new int[vertices];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
// V-1번 반복
for (int i = 0; i < vertices - 1; i++) {
for (int[] edge : edges) {
int u = edge[0], v = edge[1], w = edge[2];
if (dist[u] != Integer.MAX_VALUE && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
}
}
}
// 음수 사이클 감지: V번째 반복에서도 갱신이 발생하면 음수 사이클
for (int[] edge : edges) {
int u = edge[0], v = edge[1], w = edge[2];
if (dist[u] != Integer.MAX_VALUE && dist[u] + w < dist[v]) {
throw new RuntimeException("음수 사이클 존재!");
}
}
return dist;
}
Spring/백엔드에서의 활용
서비스 의존성 시각화
@Component
public class ServiceDependencyGraph {
private final Map<String, List<Dependency>> graph = new HashMap<>();
@Data
@AllArgsConstructor
static class Dependency {
String target;
double avgLatency; // 평균 지연 시간
int callCount; // 호출 횟수
}
// Micrometer/Zipkin 등에서 수집한 데이터로 그래프 구축
public void addCall(String from, String to, double latency) {
graph.computeIfAbsent(from, k -> new ArrayList<>())
.add(new Dependency(to, latency, 1));
}
// 크리티컬 패스 분석: 가장 긴 경로 찾기
public List<String> findCriticalPath(String start) {
// DAG에서 최장 경로 = 위상 정렬 후 DP
// ...
}
}
그래프 알고리즘 선택 가이드
| 문제 | 그래프 유형 | 알고리즘 |
|---|---|---|
| 최단 경로 (양수) | 가중치 방향 | Dijkstra |
| 최단 경로 (음수) | 가중치 방향 | Bellman-Ford |
| 모든 쌍 최단 경로 | 가중치 방향 | Floyd-Warshall |
| 최소 신장 트리 | 가중치 무방향 | Kruskal/Prim |
| 위상 정렬 | 방향 비순환 | Kahn/DFS |
| 연결 요소 | 무방향 | BFS/DFS/Union-Find |
정리
- ** 방향 그래프 **는 간선에 방향이 있어, 팔로우 관계나 API 호출 관계를 표현합니다.
- ** 가중치 그래프 **는 간선에 비용이 있어, 거리, 시간, 대역폭 등을 표현합니다.
- 도로 네트워크, SNS, MSA 서비스 호출 등 ** 현실의 대부분의 관계 **는 방향 가중치 그래프로 모델링됩니다.
- 음수 가중치가 있으면 Dijkstra 대신 Bellman-Ford 를 사용해야 합니다.
- 그래프 모델링 능력은 백엔드 아키텍처 이해와 문제 해결에 직접적으로 도움이 됩니다.
댓글 로딩 중...