Home > Backend Development > πŸ“š[Backend Development] CommentPath 클래슀 뢄석 및 μ„€λͺ…

πŸ“š[Backend Development] CommentPath 클래슀 뢄석 및 μ„€λͺ…
Backend Ddevelopment

β€œπŸ“š[Backend Development] CommentPath 클래슀 뢄석 및 μ„€λͺ…”

πŸ“Œ CommentPath 클래슀 뢄석 및 μ„€λͺ….

  • CommentPath ν΄λž˜μŠ€λŠ” κ³„μΈ΅ν˜• λŒ“κΈ€ μ‹œμŠ€ν…œμ—μ„œ 각 λŒ“κΈ€μ˜ 경둜(path)λ₯Ό κ΄€λ¦¬ν•˜λŠ” 역할을 ν•©λ‹ˆλ‹€.
  • 각 λŒ“κΈ€μ€ pathλΌλŠ” λ¬Έμžμ—΄λ‘œ ν‘œν˜„λ˜λ©°, pathλŠ” μΌμ •ν•œ κ·œμΉ™μ„ 따라 λŒ“κΈ€μ˜ λΆ€λͺ¨-μžμ‹ 관계λ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
  • 이 ν΄λž˜μŠ€λŠ” λŒ“κΈ€μ˜ 깊이(depth), λΆ€λͺ¨ λŒ“κΈ€(parentPath), μƒˆλ‘œμš΄ μžμ‹ λŒ“κΈ€(createChildCommentPath) 등을 κ΄€λ¦¬ν•˜λŠ” κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.

βœ…1️⃣ 클래슀 μ „λ°˜μ μΈ κ°œμš”.

πŸ“Œ 핡심 κ°œλ…

1️⃣ path ν•„λ“œ

  • pathλŠ” λŒ“κΈ€μ˜ 계측 ꡬ쑰λ₯Ό ν‘œν˜„ν•˜λŠ” λ¬Έμžμ—΄μž…λ‹ˆλ‹€.
  • 각 λŒ“κΈ€μ€ 5μžλ¦¬μ”©(DEPHT_CHUNK_SIZE = 5)의 λ¬Έμžμ—΄μ„ 가지며, λŒ“κΈ€μ΄ κΉŠμ–΄μ§ˆμˆ˜λ‘ pathκ°€ κΈΈμ–΄μ§‘λ‹ˆλ‹€.

πŸ“ μ˜ˆμ‹œ:

루트 λŒ“κΈ€: "00000"
첫 번째 μžμ‹ λŒ“κΈ€: "0000000000"
두 번째 μžμ‹ λŒ“κΈ€: "000000000000000"
  • 이λ₯Ό 톡해 λŒ“κΈ€μ΄ μ–΄λŠ 계측에 μ†ν•˜λŠ”μ§€, λΆ€λͺ¨κ°€ λˆ„κ΅¬μΈμ§€, μžμ‹μ΄ μ–΄λ–»κ²Œ λ°°μΉ˜λ μ§€λ₯Ό κ²°μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

2️⃣ CHARSET (문자 집합)

  • 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz (총 62개 문자)
  • path의 각 5자리(chunk)λŠ” 이 문자 집합을 μ‚¬μš©ν•΄ ν‘œν˜„λ©λ‹ˆλ‹€.
  • μƒˆλ‘œμš΄ λŒ“κΈ€μ΄ 좔가될 λ•Œ, pathλŠ” 문자 집합 λ‚΄μ—μ„œ 증가(increase)ν•˜λŠ” λ°©μ‹μœΌλ‘œ μƒμ„±λ©λ‹ˆλ‹€.

βœ…2️⃣ μ£Όμš” ν•„λ“œ

private static final String CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final int DEPTH_CHUNK_SIZE = 5; // 각 depthκ°€ 5자리둜 ν‘œν˜„λ¨
private static final int MAX_DEPTH = 5; // μ΅œλŒ€ depth 5κΉŒμ§€ ν—ˆμš©

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);
  • MIN_CHUNK = β€œ00000” : μ΅œμ†Œ chunk κ°’
  • MAX_CHUNK = β€œzzzzz” : μ΅œλŒ€ chunk κ°’ (즉, 더 이상 증가 λΆˆκ°€λŠ₯ν•œ κ°’)
  • λŒ“κΈ€μ˜ pathλŠ” MIN_CHUNKλΆ€ν„° μ‹œμž‘ν•΄ μ μ§„μ μœΌλ‘œ μ¦κ°€ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.

βœ…3️⃣ μ£Όμš” λ©”μ„œλ“œ 뢄석

  • 각 λ©”μ„œλ“œμ˜ μ—­ν• , μ‚¬μš© μ‹œκΈ°, λ™μž‘ 방식을 μ„€λͺ…ν•˜κ² μŠ΅λ‹ˆλ‹€.

1️⃣ create(String path)

public static CommentPath create(String path) {
    if (isDepthOverflowed(path)) {
        throw new IllegalStateException("depth overflowed");
    }
    CommentPath commentPath = new CommentPath();
    commentPath.path = path;
    return commentPath;
}
  • βœ… μ—­ν• :
    • 주어진 path둜 CommentPath 객체λ₯Ό μƒμ„±ν•œλ‹€.
    • path의 κΉŠμ΄κ°€ MAX_DEPTHλ₯Ό μ΄ˆκ³Όν•˜λ©΄ μ˜ˆμ™Έ λ°œμƒ.
  • βœ… μ‚¬μš© μ‹œκΈ°:
    • λŒ“κΈ€μ„ DB에 μ €μž₯ν•  λ•Œ, CommentPathλ₯Ό 생성할 λ•Œ μ‚¬μš©.
  • βœ… λ™μž‘ 방식:
      1. isDepthOverflowed(path)λ₯Ό ν˜ΈμΆœν•˜μ—¬ μ΅œλŒ€ 깊이λ₯Ό μ΄ˆκ³Όν•˜λŠ”μ§€ ν™•μΈν•œλ‹€.
      1. λ¬Έμ œκ°€ μ—†μœΌλ©΄ CommentPath 객체λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜ν•œλ‹€.

2️⃣ calculateDepth(String path)

private static int calculateDepth(String path) {
    return path.length() / DEPTH_CHUNK_SIZE;
}
  • βœ… μ—­ν• :
    • ν˜„μž¬ path의 깊이λ₯Ό κ³„μ‚°ν•œλ‹€.
    • path.length()λ₯Ό DEPTH_CHUNK_SIZE둜 λ‚˜λˆ„λ©΄ κΉŠμ΄κ°€ λœλ‹€.
  • βœ… μ‚¬μš© μ‹œκΈ°:
    • λŒ“κΈ€μ΄ λͺ‡ 번째 κΉŠμ΄μΈμ§€ 확인 ν•  λ•Œ.
    • isRoot(), isDepthOverflowed() λ“±μ˜ λ©”μ„œλ“œμ—μ„œ μ‚¬μš©.
  • βœ… λ™μž‘ 방식:
    • path.length()λ₯Ό 5둜 λ‚˜λˆˆλ‹€.
    • 예: β€œ0000000000” (10κΈ€μž) ➞ calculateDepth(β€œ0000000000”) ➞ 10 / 5 = 2

3️⃣ getDepth()

public int getDepth() {
    return calculateDepth(path);
}
  • βœ… μ—­ν• :
    • ν˜„μž¬ λŒ“κΈ€μ˜ 깊이λ₯Ό λ°˜ν™˜ν•œλ‹€.
  • βœ… μ‚¬μš© μ‹œκΈ°:
    • λŒ“κΈ€μ˜ 깊이λ₯Ό 확인할 λ•Œ.
  • βœ… λ™μž‘ 방식:
    • calculateDepth(path)λ₯Ό ν˜ΈμΆœν•˜μ—¬ 깊이λ₯Ό κ΅¬ν•œλ‹€.

4️⃣ isRoot()

public boolean isRoot() {
    return calculateDepth(path) == 1;
}
  • βœ… μ—­ν• :
    • ν˜„μž¬ λŒ“κΈ€μ΄ 루트 λŒ“κΈ€μΈμ§€ ν™•μΈν•œλ‹€.
  • βœ… μ‚¬μš© μ‹œκΈ°:
    • λΆ€λͺ¨ λŒ“κΈ€μ΄ μ—†λŠ” 루트 λŒ“κΈ€μΈμ§€ 확인할 λ•Œ.
  • βœ… λ™μž‘ 방식:
    • κΉŠμ΄κ°€ 1이면 루트 λŒ“κΈ€λ‘œ νŒλ‹¨.

5️⃣ getParentPath()

public String getParentPath() {
    return path.substring(0, path.length() - DEPTH_CHUNK_SIZE);
}
  • βœ… μ—­ν• :
    • λΆ€λͺ¨ λŒ“κΈ€μ˜ pathλ₯Ό λ°˜ν™˜ν•œλ‹€.
  • βœ… μ‚¬μš© μ‹œκΈ°:
    • λŒ“κΈ€μ˜ λΆ€λͺ¨λ₯Ό 찾을 λ•Œ.
  • βœ… λ™μž‘ 방식:
    • ν˜„μž¬ pathμ—μ„œ λ§ˆμ§€λ§‰ 5자리λ₯Ό μž˜λΌλ‚΄μ–΄ λ°˜ν™˜ν•œλ‹€.

6️⃣ createChildCommentPath(String descendantsTopPath)

public CommentPath createChildCommentPath(String descendantsTopPath) {
    if (descendantsTopPath == null) {
        return CommentPath.creat(path + MIN_CHUNK);
    }
    String childrenTopPath = findChildrenTopPath(descendantsTopPath);
    return CommentPath.create(increase(childrenTopPath));
}
  • βœ… μ—­ν• :
    • ν˜„μž¬ λŒ“κΈ€μ˜ μžμ‹ λŒ“κΈ€μ˜ pathλ₯Ό μƒμ„±ν•œλ‹€.
  • βœ… μ‚¬μš© μ‹œκΈ°:
    • μƒˆλ‘œμš΄ λŒ€λŒ“κΈ€μ„ μΆ”κ°€ν•  λ•Œ.
  • βœ… λ™μž‘ 방식:
      1. descendantsTopPathκ°€ null이면 MIN_CHUNKλ₯Ό λΆ™μ—¬μ„œ μžμ‹ λŒ“κΈ€μ„ 생성.
      1. μžμ‹ λŒ“κΈ€μ˜ pathλ₯Ό κ°€μ Έμ˜¨ ν›„ increase()λ₯Ό ν˜ΈμΆœν•˜μ—¬ 증가.

7️⃣ findChildrenTopPath(String descendantsTopPath)

private String findChildrenTopPath(String descendantsTopPath) {
    return descendantsTopPath.substring(0, (getDepth() + 1) * DEPTH_CHUNK_SIZE);
}
  • βœ… μ—­ν• :
    • μžμ† λŒ“κΈ€ 쀑 κ°€μž₯ μƒμœ„ λŒ“κΈ€μ˜ pathλ₯Ό λ°˜ν™˜ν•œλ‹€.
  • βœ… μ‚¬μš© μ‹œκΈ°:
    • μƒˆλ‘œμš΄ λŒ“κΈ€μ„ μΆ”κ°€ν•  λ•Œ, μ–΄λ–€ λŒ“κΈ€μ΄ ν˜„μž¬ λŒ“κΈ€μ˜ κ°€μž₯ 졜근 μžμ‹ λŒ“κΈ€μΈμ§€ 찾을 λ•Œ.
  • βœ… λ™μž‘ 방식:
    • ν˜„μž¬ κΉŠμ΄λ³΄λ‹€ ν•œ 단계 더 κΉŠμ€ path 뢀뢄을 μž˜λΌμ„œ λ°˜ν™˜.

8️⃣ increase(String path)

private String increase(String path) {
    String lastChunk = path.substring(path.length() - DEPTH_CHUNK_SIZE);

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

    int charsetLength = CHARSET.length();
    int value = 0;

    for (char character : lastChunk.toCharArray()) {
        value = value * charsetLength + 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;
}
  • βœ… μ—­ν• :
    • λŒ“κΈ€ pathλ₯Ό μ¦κ°€μ‹œμΌœ μƒˆλ‘œμš΄ μžμ‹ λŒ“κΈ€ 생성
  • βœ… μ‚¬μš© μ‹œκΈ°:
    • μƒˆλ‘œμš΄ λŒ€λŒ“κΈ€μ„ μΆ”κ°€ν•  λ•Œ

      πŸ“ λ™μž‘ 방식:

      πŸ“Œ μž…λ ₯(path)

  • pathλŠ” λŒ“κΈ€μ˜ 계측 ꡬ쑰λ₯Ό λ‚˜νƒ€λ‚΄λŠ” λ¬Έμžμ—΄.
  • 예λ₯Ό λ“€μ–΄ β€œ0000000000”(2번째 깊이의 λŒ“κΈ€)μ΄λΌλŠ” μ£Όμ–΄μ‘Œλ‹€κ³  κ°€μ •.

πŸ“Œ 1단계: λ§ˆμ§€λ§‰ 5자리(Chunk) μΆ”μΆœ

String lastChunk = path.substring(path.length() - DEPTH_CHUNK_SIZE);
  • pathμ—μ„œ λ§ˆμ§€λ§‰ DEPTH_CHUNK_SIZE(=5)만큼의 λ¬Έμžμ—΄μ„ μž˜λΌλ‚Έλ‹€.
  • 예제:
path = "0000000000";
lastChunk = path.substring(5); // "00000"

πŸ“Œ 2단계: lastChunk 값이 μ΅œλŒ€κ°’μΈμ§€ 확인

if (isChunkOverflowed(lastChunk)) {
    throw new IllegalStateException("chunk overflowed");
}
  • isChunkOverflowed(lastChunk) λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ lastChunkκ°€ β€œzzzzz”(μ΅œλŒ€κ°’)인지 확인.
  • λ§Œμ•½ β€œzzzzz”라면 더 이상 증가할 수 μ—†μœΌλ―€λ‘œ μ˜ˆμ™Έλ₯Ό λ˜μ§„λ‹€.

πŸ“Œ 3단계: lastChunk 값을 10μ§„μˆ˜λ‘œ λ³€ν™˜

int charsetLength = CHARSET.length();
int value = 0;

for (char character : lastChunk.toCharArray()) {
    value = value * charsetLength + CHARSET.indexOf(character);
}
  • CHARSET은 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz (총 62개 문자).
  • lastChunk(ν˜„μž¬ 5자리 λ¬Έμžμ—΄)λ₯Ό 62μ§„μˆ˜μ—μ„œ 10μ§„μˆ˜λ‘œ λ³€ν™˜ν•œλ‹€.

πŸ“ 예제 1: β€œ00000” λ³€ν™˜

  • CHARSET.index(β€˜0’) = 0
  • λ³€ν™˜ κ³Όμ •:
    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
    

πŸ“ 예제 2: β€œ0000z” λ³€ν™˜

  • β€˜zβ€™μ˜ μΈλ±μŠ€λŠ” 61
    value = (0 * 62) + 0 = 0
    value = (0 * 62) + 0 = 0
    value = (0 * 62) + 0 = 0
    value = (0 * 62) + 0 = 0
    value = (0 * 62) + 61 = 61
    

πŸ“Œ 4단계: κ°’ 증가 (+ 1 μ—°μ‚°)

value = value + 1;
  • 10μ§„μˆ˜μ˜ 값이 1μ¦κ°€ν•œλ‹€.

πŸ“ 예제 1: β€œ00000” ➞ β€œ00001”

value = 0 + 1 = 1

πŸ“ 예제 2: β€œ0000z” ➞ β€œ00010”

value = 61 + 1 = 62

πŸ“Œ 5단계: λ‹€μ‹œ 62μ§„μˆ˜ λ¬Έμžμ—΄λ‘œ λ³€ν™˜

String result = "";

for (int i = 0; i < DEPTH_CHUNK_SIZE; i++) {
    result = CHARSET.charAt(value % charsetLength) + result;
    value /= charsetLength;
}
  • μ¦κ°€λœ 10μ§„μˆ˜ 값을 λ‹€μ‹œ 62μ§„μˆ˜ λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•œλ‹€.

πŸ“ 예제 1: value = 1

  • value % 62 = 1 ➞ β€˜1’
  • value /= 62 = -
  • κ²°κ³Ό: β€œ00001”

πŸ“ 예제 2: value = 62

  • value % 62 = 0 ➞ β€˜0’
  • value /= 62 = 1
  • value % 62 = 1 ➞ β€˜1’
  • μ΅œμ’… κ²°κ³Ό: β€œ00010”

πŸ“Œ 6단계: κΈ°μ‘΄ pathμ—μ„œ λ§ˆμ§€λ§‰ chunkλ₯Ό μƒˆλ‘œμš΄ κ°’μœΌλ‘œ ꡐ체

return path.substring(0, path.length() - DEPTH_CHUNK_SIZE) + result;
  • μ›λž˜ path의 λ§ˆμ§€λ§‰ 5자리λ₯Ό μƒˆλ‘œμš΄ κ°’μœΌλ‘œ λ³€κ²½ν•œ λ¬Έμžμ—΄μ„ λ°˜ν™˜.

πŸ“ 예제

path = "0000000000";
result = "00001";
μ΅œμ’… λ°˜ν™˜κ°’: "0000000001"

πŸ“Œ 전체 λ™μž‘ 예제

πŸ“Œ 예제 1

increase("0000000000"); // κΈ°μ‘΄ λŒ“κΈ€μ˜ path
    1. β€œ0000000000β€μ—μ„œ λ§ˆμ§€λ§‰ 5자리 β€œ00000”을 μΆ”μΆœ.
    1. β€œ00000” β†’ 10μ§„μˆ˜ λ³€ν™˜ ➞ 0
    1. 0 + 1 = 1
    1. 1 ➞ 62μ§„μˆ˜ λ³€ν™˜ ➞ β€œ00001”
    1. β€œ0000000000” ➞ β€œ0000000001β€λ‘œ λ³€ν™˜
  • βœ… μ΅œμ’… κ²°κ³Ό : β€œ0000000001”

πŸ“Œ 예제 2

increase("000000000z"); // κΈ°μ‘΄ λŒ“κΈ€μ˜ path
    1. β€œ000000000zβ€μ—μ„œ λ§ˆμ§€λ§‰ 5자리 β€œ0000z”을 μΆ”μΆœ.
    1. β€œ0000z” β†’ 10μ§„μˆ˜ λ³€ν™˜ ➞ 61
    1. 61 + 1 = 62
    1. 62 ➞ 62μ§„μˆ˜ λ³€ν™˜ ➞ β€œ00010”
    1. β€œ000000000z” ➞ β€œ0000000010β€λ‘œ λ³€ν™˜
  • βœ… μ΅œμ’… κ²°κ³Ό : β€œ0000000010”