Home > Backend Development > ๐Ÿ“š[Backend Development] increase๋ฉ”์„œ๋“œ ์‹คํ–‰๊ณผ์ • ๋ถ„์„

๐Ÿ“š[Backend Development] increase๋ฉ”์„œ๋“œ ์‹คํ–‰๊ณผ์ • ๋ถ„์„
Backend Ddevelopment

โ€œ๐Ÿ“š[Backend Development] increase๋ฉ”์„œ๋“œ ์‹คํ–‰๊ณผ์ • ๋ถ„์„โ€

๐Ÿ“Œ CommentPath ํด๋ž˜์Šค.

package kobe.board.comment.entity;

import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Getter
@ToString
@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CommentPath {
	private String path;

	private static final String CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

	private static final int DEPTH_CHUNK_SIZE = 5;
	private static final int MAX_DEPTH = 5;

	// MIN_CHUNK = "00000", MAX_CHUNK = "zzzzz"
	private static final String MIN_CHUNK = String.valueOf(CHARSET.charAt(0)).repeat(DEPTH_CHUNK_SIZE);
	private static final String MAX_CHUNK = String.valueOf(CHARSET.charAt(CHARSET.length() - 1)).repeat(DEPTH_CHUNK_SIZE);

	public static CommentPath create(String path) {
		if (isDepthOverflowed(path)) {
			throw new IllegalStateException("depth overflowed");
		}

		CommentPath commentPath = new CommentPath();
		commentPath.path = path;
		return commentPath;
	}

	private static boolean isDepthOverflowed(String path) {
		return calculateDepth(path) > MAX_DEPTH;
	}

	private static int calculateDepth(String path) {
		// 25๊ฐœ์˜ ๋ฌธ์ž์—ด / 5 = 5depth
		return path.length() / DEPTH_CHUNK_SIZE;
	}

	// CommentPath ํด๋ž˜์Šค์˜ path์˜ depth๋ฅผ ๊ตฌํ•˜๋Š” ๋งค์„œ๋“œ
	public int getDepth() {
		return calculateDepth(path);
	}

	// root์ธ์ง€ ํ™•์ธํ•˜๋Š” ๋งค์„œ๋“œ
	public boolean isRoot() {
		// ํ˜„์žฌ์˜ depth๊ฐ€ 1์ธ์ง€ ํ™•์ธํ•ด์ฃผ๋ฉด ๋จ
		return calculateDepth(path) == 1;
	}

	// ํ˜„์žฌ path์˜ parentPath๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š” ๋งค์„œ๋“œ
	public String getParentPath() {
		// ๋ 5์ž๋ฆฌ๋งŒ ์ž˜๋ผ๋‚ด๋ฉด ๋จ
		return path.substring(0, path.length() - DEPTH_CHUNK_SIZE);
	}

	// ํ˜„์žฌ path์˜ ํ•˜์œ„ ๋Œ“๊ธ€์˜ path์„ ๋งŒ๋“œ๋Š” ๋งค์„œ๋“œ
	public CommentPath createChildCommentPath(String descendantsTopPath) {
		if (descendantsTopPath == null) {
			return CommentPath.create(path + MIN_CHUNK);
		}
		String childrenTopPath = findChildrenTopPath(descendantsTopPath);
		return CommentPath.create(increase(childrenTopPath));
	}

	// 00a0z0000200000 <- descendantsTopPath
	private String findChildrenTopPath(String descendantsTopPath) {
		return descendantsTopPath.substring(0, (getDepth() + 1) * DEPTH_CHUNK_SIZE);
	}

	private String increase(String path) {
		// path์—์„œ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ 5์ž๋ฆฌ๋ฅผ ์ž๋ฅธ ๊ฒƒ
		String lastChunk = path.substring(path.length() - DEPTH_CHUNK_SIZE);

		if (isChunkOverflowed(lastChunk)) {
			throw new IllegalStateException("chunk overflowed");
		}

		// Character set์˜ ๊ธธ์ด
		int charsetLength = CHARSET.length();

		// lastChunk๋ฅผ 10์ง„์ˆ˜๋กœ ๋จผ์ € ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ’์„ ์ €์žฅ
		int value = 0;

		for (char character : lastChunk.toCharArray()) {
			value = value * charsetLength + CHARSET.indexOf(character);
			System.out.println("value ====> " + value);
			System.out.println("CHARSET.indexOf ====> " +  CHARSET.indexOf(character));
		}

		value = value + 1;

		String result = "";

		for (int i = 0; i < DEPTH_CHUNK_SIZE; i++) {
			result = CHARSET.charAt(value % charsetLength) + result;
			value /= charsetLength;
		}

		return path.substring(0, path.length() - DEPTH_CHUNK_SIZE) + result;
	}

	private boolean isChunkOverflowed(String lastChunk) {
		return MAX_CHUNK.equals(lastChunk);
	}
}

โœ…1๏ธโƒฃ ์ฝ”๋“œ ์‹คํ–‰ ํ๋ฆ„ ํ™•์ธ

for (int i = 0; i < DEPTH_CHUNK_SIZE; i++) {
    result = CHARSET.charAt(value % charsetLength) + result;
    value /= charsetLength;
}

๐Ÿ“Œ ์ฝ”๋“œ ํ•ต์‹ฌ ์—ญํ• 

  • value๋ฅผ CHARSET(62์ง„์ˆ˜) ๊ธฐ๋ฐ˜์œผ๋กœ DEPTH_CHUNK_SIZE(=5) ๊ธธ์ด์˜ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •
  • ๊ฐ ๋ฐ˜๋ณต์—์„œ value์˜ ๋งˆ์ง€๋ง‰ ์ž๋ฆฌ๋ฅผ CHARSET์—์„œ ์ฐพ์•„ ๋ฌธ์ž์—ด(result)์— ์ถ”๊ฐ€ํ•˜๊ณ , value๋ฅผ 62๋กœ ๋‚˜๋ˆ ์„œ ๋‹ค์Œ ๋ฌธ์ž๋ฅผ ๊ตฌํ•จ.

โœ…2๏ธโƒฃ descendantsTopPath = โ€œ00000โ€ ์ผ ๋•Œ increas(โ€œ00000โ€)์˜ ์‹คํ–‰ ๊ณผ์ •

๐Ÿ“Œ increase(โ€œ00000โ€) ์‹คํ–‰ ๊ณผ์ •

1. lastChunk ์ถ”์ถœ

String lastChunk = path.substring(path.length() - DEPTH_CHUNK_SIZE)
  • โ€œ00000โ€ โžž lastChunk = โ€œ00000โ€

2. lastChunk๋ฅผ 10์ง„์ˆ˜(value)๋กœ ๋ณ€ํ™˜

int value = 0;
for (char character : lastChunk.toCharArray()) {
    value = value * charsetLength + CHARSET.indexOf(cha)
}
  • CHARSET.indexOf(โ€˜0โ€™) = 0
  • value ๊ฐ’ ๊ณ„์‚ฐ:
value = (0 * 62) + 0 = 0
value = (0 * 62) + 0 = 0
value = (0 * 62) + 0 = 0
value = (0 * 62) + 0 = 0
value = (0 * 62) + 0 = 0
  • ์ตœ์ข…์ ์œผ๋กœ value = 0

3. value + 1 ์ฆ๊ฐ€.

value = value + 1;
  • value = 0 + 1 = 1

4. value๋ฅผ ๋‹ค์‹  62์ง„์ˆ˜ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜

for (int i = 0; i < DEPTH_CHUNK_SIZE; i++) {
    result = CHARSET.charAt(value % charserLength) + result;
    value /= charsetLength;
}
  • ๋ฐ˜๋ณต ๊ณผ์ •(DEPTH_CHUNK_SIZE = 5)
i = 0: value % 62 = 1 โ†’ CHARSET[1] = '1' โ†’ result = "1"
i = 1: value /= 62 = 0 โ†’ CHARSET[0] = '0' โ†’ result = "01"
i = 2: value = 0 โ†’ CHARSET[0] = '0' โ†’ result = "001"
i = 3: value = 0 โ†’ CHARSET[0] = '0' โ†’ result = "0001"
i = 4: value = 0 โ†’ CHARSET[0] = '0' โ†’ result = "00001"
  • ์ตœ์ข… result = โ€œ00001โ€