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”