멀티캠퍼스

[2026.06.08] - TIL 46일차 답변형 게시판 구현 정리

buckwheat 2026. 6. 9. 00:12

 

목차
1 답변형 게시판 개요
2 ANSWERBOARD 테이블 구조
3 GROUPNO, GROUPSQ, TITLETAB
4 새 글 작성 흐름
5 답글 작성 흐름
6 AnswerDto
7 AnswerDao
8 AnswerService
9 AnswerController
10 JSP 화면 구성
11 오늘 실습의 핵심 정리

1 답변형 게시판 개요

1) 답변형 게시판

    (1) 답변형 게시판의 의미
    답변형 게시판은 일반 게시글뿐만 아니라 특정 게시글에 답글을 작성할 수 있는 게시판이다.
    단순 게시판은 글 목록이 작성 순서대로만 출력되지만, 답변형 게시판은 원글과 답글의 관계를 유지해야 한다.

    (2) 오늘 실습의 목적
    오늘은 게시글을 작성하고, 특정 게시글에 답글을 작성하는 구조를 실습했다.
    답글이 원글 바로 아래에 출력되도록 하기 위해 GROUPNO, GROUPSQ, TITLETAB 값을 사용했다.

    (3) 기존 게시판과의 차이
    기존 게시판은 글 번호, 작성자, 제목, 내용, 작성일 정도만 관리하면 되었다.
    하지만 답변형 게시판은 원글과 답글의 관계, 답글의 출력 순서, 답글의 들여쓰기까지 관리해야 한다.

2 ANSWERBOARD 테이블 구조

1) ANSWERBOARD 테이블

    (1) 게시글과 답글을 함께 저장하는 테이블
    ANSWERBOARD 테이블은 원글과 답글을 모두 저장하는 테이블이다.
    원글과 답글을 따로 테이블로 나누지 않고, 하나의 테이블 안에서 그룹 번호와 순서를 이용해 관계를 표현한다.

    (2) BOARDNO
    BOARDNO는 글 번호이다.
    PRIMARY KEY로 설정되어 게시글 하나를 구분하는 고유한 값이다.
    BOARDNOSQ 시퀀스를 이용해 자동으로 증가한다.

    (3) GROUPNO
    GROUPNO는 글의 그룹 번호이다.
    원글과 그 원글에 달린 답글들은 같은 GROUPNO를 가진다.
    새로운 원글을 작성할 때는 GROUPNOSQ 시퀀스를 사용해 새로운 그룹 번호를 만든다.

    (4) GROUPSQ
    GROUPSQ는 같은 그룹 안에서 글이 출력되는 순서이다.
    원글은 보통 1번을 가지고, 답글은 부모글의 GROUPSQ보다 뒤에 배치된다.

    (5) TITLETAB
    TITLETAB은 제목 앞의 들여쓰기 정도를 의미한다.
    원글은 0이고, 답글은 부모글의 TITLETAB보다 더 큰 값을 가진다.
    이를 통해 화면에서 답글이 원글보다 안쪽으로 들어가 보이게 만들 수 있다.

    (6) TITLE
    TITLE은 글 제목이다.
    답글은 보통 제목 앞에 RE:를 붙여 원글에 대한 답변임을 표현한다.

    (7) CONTENT
    CONTENT는 글 내용이다.
    원글과 답글의 본문 내용을 저장한다.

    (8) WRITER
    WRITER는 작성자이다.
    누가 글을 작성했는지 저장한다.

    (9) REGDATE
    REGDATE는 작성일이다.
    SYSDATE를 사용해 글이 작성된 날짜를 저장한다.

3 GROUPNO, GROUPSQ, TITLETAB

1) GROUPNO

    (1) 글 묶음 번호
    GROUPNO는 원글과 답글을 하나의 묶음으로 관리하기 위한 값이다.
    같은 원글에서 시작된 답글들은 모두 같은 GROUPNO를 가진다.

    (2) 새 글 작성 시
    새 글을 작성하면 새로운 그룹이 시작된다.
    따라서 GROUPNOSQ.NEXTVAL을 사용해 새로운 GROUPNO를 부여한다.

    (3) 답글 작성 시
    답글은 부모글과 같은 그룹에 속해야 한다.
    따라서 부모글의 GROUPNO를 조회해서 그대로 사용한다.

2) GROUPSQ

    (1) 그룹 안의 출력 순서
    GROUPSQ는 같은 GROUPNO 안에서 글이 어떤 순서로 출력될지 결정한다.
    ORDER BY GROUPNO DESC, GROUPSQ로 정렬하면 최신 그룹이 먼저 나오고, 같은 그룹 안에서는 GROUPSQ 순서대로 출력된다.

    (2) 새 글 작성 시
    새 원글은 그룹 안의 첫 번째 글이므로 GROUPSQ 값을 1로 설정한다.

    (3) 답글 작성 시
    답글은 부모글 바로 아래에 들어가야 한다.
    그래서 부모글의 GROUPSQ보다 큰 기존 글들의 GROUPSQ를 1씩 증가시킨 뒤, 새 답글을 부모글의 GROUPSQ + 1 위치에 넣는다.

3) TITLETAB

    (1) 답글 들여쓰기 값
    TITLETAB은 화면에서 제목 앞에 공백을 얼마나 줄지 결정하는 값이다.
    원글은 0이고, 답글은 부모글보다 더 큰 TITLETAB 값을 가진다.

    (2) 답글 작성 시
    답글의 TITLETAB은 부모글의 TITLETAB + 3으로 설정했다.
    답글이 깊어질수록 제목 앞의 공백이 늘어나 계층 구조가 보이게 된다.

    (3) 화면 출력
    boardlist.jsp에서는 c:forEach를 사용해 TITLETAB 값만큼 공백을 출력했다.
    이를 통해 원글과 답글의 관계를 시각적으로 표현했다.

4 새 글 작성 흐름

1) 새 글 작성 SQL

    (1) BOARDNOSQ
    BOARDNOSQ는 글 번호를 자동으로 생성한다.
    원글이든 답글이든 새 글이 추가될 때마다 새로운 BOARDNO가 필요하다.

    (2) GROUPNOSQ
    GROUPNOSQ는 새로운 원글의 그룹 번호를 생성한다.
    새 글은 새로운 그룹의 시작이기 때문에 GROUPNOSQ.NEXTVAL을 사용한다.

    (3) 기본값
    새 글은 GROUPSQ를 1로 설정한다.
    TITLETAB은 0으로 설정한다.
    원글이기 때문에 들여쓰기가 필요하지 않다.

2) 새 글 작성 메소드

    (1) Controller
    사용자가 boardwrite.jsp에서 글을 작성하면 answer?command=boardwrite 요청이 발생한다.
    AnswerController는 title, content, writer 값을 받아 AnswerDto에 담는다.

    (2) Service
    AnswerService의 insert 메소드는 Connection을 생성하고 DAO의 insert 메소드를 호출한다.
    INSERT 성공 시 commit하고, 실패 시 rollback한다.

    (3) DAO
    AnswerDao의 insert 메소드는 ANSWERBOARD 테이블에 새 원글을 추가한다.
    이때 BOARDNO와 GROUPNO는 시퀀스를 사용하고, GROUPSQ는 1, TITLETAB은 0으로 저장한다.

5 답글 작성 흐름

1) 답글 작성의 핵심

    (1) 부모글 기준
    답글은 혼자 만들어지는 글이 아니라 어떤 부모글에 대한 답변이다.
    따라서 답글을 작성하려면 먼저 부모글의 BOARDNO가 필요하다.

    (2) 부모글 정보 조회
    AnswerService의 insertAnswer 메소드에서는 먼저 부모글을 조회한다.
    부모글의 GROUPNO, GROUPSQ, TITLETAB 값을 알아야 답글의 위치와 들여쓰기를 결정할 수 있다.

    (3) 기존 답글 순서 밀기
    부모글 아래에 새 답글이 들어가야 하므로, 같은 GROUPNO 안에서 부모글보다 뒤에 있는 글들의 GROUPSQ를 1씩 증가시킨다.
    이 과정을 통해 새 답글이 들어갈 자리를 만든다.

    (4) 답글 INSERT
    기존 글들의 순서를 밀어낸 뒤 새 답글을 INSERT한다.
    답글의 GROUPNO는 부모글과 같고, GROUPSQ는 부모글의 GROUPSQ + 1이며, TITLETAB은 부모글의 TITLETAB + 3이다.

2) updateAnswer

    (1) 순서 변경 메소드
    updateAnswer는 답글을 넣기 전에 기존 글들의 GROUPSQ를 조정하는 메소드이다.
    같은 그룹 안에서 부모글보다 뒤에 있는 글들의 순서를 하나씩 뒤로 민다.

    (2) SQL 조건
    GROUPNO가 부모글의 GROUPNO와 같고, GROUPSQ가 부모글의 GROUPSQ보다 큰 글들이 대상이다.
    이 글들의 GROUPSQ를 GROUPSQ + 1로 변경한다.

    (3) 사용하는 이유
    새 답글이 부모글 바로 아래에 들어가려면 기존 글들이 한 칸씩 뒤로 밀려야 한다.
    그렇지 않으면 답글 순서가 꼬일 수 있다.

3) insertAnswer

    (1) 답글 추가 메소드
    insertAnswer는 실제로 답글을 ANSWERBOARD 테이블에 추가하는 메소드이다.
    부모글에서 가져온 GROUPNO, GROUPSQ, TITLETAB 정보를 기준으로 답글 위치를 정한다.

    (2) GROUPNO
    답글의 GROUPNO는 부모글과 같다.
    그래야 원글과 답글이 같은 묶음으로 정렬된다.

    (3) GROUPSQ
    답글의 GROUPSQ는 부모글의 GROUPSQ + 1이다.
    부모글 바로 아래에 출력되도록 하기 위한 값이다.

    (4) TITLETAB
    답글의 TITLETAB은 부모글의 TITLETAB + 3이다.
    답글이 부모글보다 안쪽으로 들어가 보이게 하기 위한 값이다.

6 AnswerDto

1) AnswerDto의 역할

    (1) 답변형 게시판 데이터 객체
    AnswerDto는 ANSWERBOARD 테이블의 한 행 데이터를 Java 객체로 담는 클래스이다.
    원글과 답글 정보를 모두 담을 수 있다.

    (2) DTO를 사용하는 이유
    DB에서 가져온 글 정보를 Controller, Service, DAO, JSP 사이에서 전달하기 위해 사용한다.
    여러 컬럼 값을 하나의 객체로 묶어 관리할 수 있다.

2) 필드

    (1) boardno
    boardno는 글 번호이다.
    글 하나를 구분하는 고유 값이다.

    (2) groupno
    groupno는 그룹 번호이다.
    원글과 답글을 같은 묶음으로 관리하는 데 사용한다.

    (3) groupsq
    groupsq는 그룹 안에서의 출력 순서이다.
    답글이 어느 위치에 들어갈지 결정한다.

    (4) titletab
    titletab은 제목 들여쓰기 값이다.
    답글의 깊이를 화면에 표현할 때 사용한다.

    (5) title
    title은 글 제목이다.
    답글 작성 화면에서는 부모글 제목 앞에 RE:를 붙여 기본값으로 보여준다.

    (6) content
    content는 글 내용이다.
    상세 보기 화면에서 textarea로 출력된다.

    (7) writer
    writer는 작성자이다.
    글을 작성한 사람의 이름을 저장한다.

    (8) regdate
    regdate는 작성일이다.
    글 작성 시점의 날짜를 저장한다.

7 AnswerDao

1) AnswerDao의 역할

    (1) DB 처리 담당
    AnswerDao는 ANSWERBOARD 테이블에 직접 접근해 SQL을 실행하는 클래스이다.
    목록 조회, 새 글 작성, 상세 조회, 답글 순서 변경, 답글 작성을 처리한다.

2) selectAll

    (1) 전체 목록 조회
    selectAll은 ANSWERBOARD 테이블의 모든 글을 조회한다.
    원글과 답글이 함께 조회된다.

    (2) 정렬 기준
    ORDER BY GROUPNO DESC, GROUPSQ를 사용한다.
    GROUPNO DESC는 최신 원글 그룹이 위로 오게 하고, GROUPSQ는 같은 그룹 안에서 원글과 답글 순서를 맞춘다.

    (3) List 반환
    여러 개의 글을 조회하므로 List<AnswerDto>에 담아 반환한다.
    JSP에서는 이 list를 반복해서 출력한다.

3) insert

    (1) 새 원글 작성
    insert는 답글이 아닌 새로운 원글을 작성하는 메소드이다.
    BOARDNO와 GROUPNO는 각각 시퀀스로 생성하고, GROUPSQ는 1, TITLETAB은 0으로 저장한다.

4) selectOne

    (1) 글 하나 조회
    selectOne은 BOARDNO를 기준으로 글 하나를 조회한다.
    상세 보기와 답글 작성 시 부모글 정보를 가져올 때 사용한다.

5) updateAnswer

    (1) 답글 순서 조정
    updateAnswer는 새 답글을 넣기 전에 기존 글들의 GROUPSQ를 수정한다.
    같은 그룹 안에서 부모글보다 뒤에 있는 글들을 한 칸씩 뒤로 밀어준다.

6) insertAnswer

    (1) 답글 저장
    insertAnswer는 새 답글을 DB에 저장한다.
    부모글과 같은 GROUPNO를 사용하고, GROUPSQ와 TITLETAB은 부모글 값을 기준으로 계산한다.

8 AnswerService

1) AnswerService의 역할

    (1) DAO 호출과 트랜잭션 처리
    AnswerService는 Controller와 DAO 사이에서 동작한다.
    DAO를 호출하고, INSERT나 UPDATE 작업의 성공 여부에 따라 commit 또는 rollback을 처리한다.

    (2) Connection 관리
    Service에서 Connection을 생성하고 DAO에 전달한다.
    DAO는 전달받은 Connection으로 SQL을 실행하고, Service는 마지막에 Connection을 닫는다.

2) selectAll

    (1) 목록 조회 서비스
    selectAll은 DAO의 selectAll을 호출해 전체 글 목록을 가져온다.
    조회 기능이므로 commit이나 rollback은 필요하지 않다.

3) insert

    (1) 새 글 작성 서비스
    insert는 DAO의 insert를 호출해 새로운 원글을 저장한다.
    저장 성공 시 commit하고 실패 시 rollback한다.

4) selectOne

    (1) 상세 조회 서비스
    selectOne은 BOARDNO를 기준으로 글 하나를 조회한다.
    상세 보기 화면과 답글 작성 화면에서 사용된다.

5) insertAnswer

    (1) 답글 작성 서비스
    insertAnswer는 답글 작성의 전체 흐름을 담당한다.
    부모글 조회, 기존 글 순서 변경, 답글 INSERT를 하나의 흐름으로 처리한다.

    (2) 부모글 조회
    먼저 parentboardno를 이용해 부모글 정보를 조회한다.
    부모글의 GROUPNO, GROUPSQ, TITLETAB 값을 기준으로 답글 위치를 정한다.

    (3) 순서 변경
    dao.updateAnswer를 호출해 같은 그룹 안에서 부모글보다 뒤에 있는 글들의 GROUPSQ를 증가시킨다.
    이 과정은 새 답글이 들어갈 공간을 만드는 작업이다.

    (4) 답글 정보 설정
    입력받은 dto에 부모글의 GROUPNO, GROUPSQ, TITLETAB 값을 세팅한다.
    DAO의 insertAnswer에서는 이 값을 기준으로 답글의 GROUPSQ + 1, TITLETAB + 3을 계산해 저장한다.

    (5) 트랜잭션 처리
    답글 작성은 순서 변경과 INSERT가 함께 이루어져야 한다.
    INSERT가 성공하면 commit하고, 실패하면 rollback한다.
    이처럼 여러 DB 작업이 하나의 흐름으로 묶일 때 트랜잭션 처리가 중요하다.

9 AnswerController

1) AnswerController의 역할

    (1) Servlet Controller
    AnswerController는 답변형 게시판의 요청을 처리하는 Servlet이다.
    @WebServlet("/answer")로 매핑되어 있다.

    (2) command 방식
    command 값에 따라 목록 조회, 글쓰기 화면 이동, 새 글 작성, 상세 조회, 답글 작성 화면 이동, 답글 작성 처리를 나누었다.

2) list

    (1) 목록 요청
    command가 list이면 service.selectAll을 호출한다.
    조회한 글 목록을 request에 list라는 이름으로 저장하고 boardlist.jsp로 forward한다.

3) writeform

    (1) 글쓰기 화면 요청
    command가 writeform이면 boardwrite.jsp로 이동한다.
    새 원글을 작성하는 화면이다.

4) boardwrite

    (1) 새 글 작성 요청
    command가 boardwrite이면 title, content, writer 값을 받아 AnswerDto에 저장한다.
    service.insert를 호출해 새 원글을 작성한다.

    (2) 결과 처리
    작성 성공 시 “새로운 글 작성 성공” 메시지를 출력하고 목록으로 이동한다.
    실패 시 글쓰기 화면으로 다시 이동할 수 있도록 처리한다.

5) detail

    (1) 상세 조회 요청
    command가 detail이면 boardno 값을 받아 해당 글 하나를 조회한다.
    조회 결과를 dto라는 이름으로 request에 저장하고 boarddetail.jsp로 forward한다.

6) answerform

    (1) 답글 작성 화면 요청
    command가 answerform이면 parentboardno 값을 받는다.
    부모글 정보를 조회해 parent라는 이름으로 request에 저장하고 answerwrite.jsp로 이동한다.

    (2) 부모글 정보가 필요한 이유
    답글 제목에 RE:부모글 제목을 기본으로 넣기 위해 부모글 제목이 필요하다.
    또한 답글 작성 처리 시 부모글 번호를 hidden input으로 전달해야 한다.

7) answerwrite

    (1) 답글 작성 요청
    command가 answerwrite이면 parentboardno와 답글의 title, content, writer 값을 받는다.
    입력값을 AnswerDto에 담고 service.insertAnswer를 호출한다.

    (2) 결과 처리
    답글 작성 성공 시 목록으로 이동한다.
    실패 시 부모글 상세 화면으로 돌아가도록 처리한다.

10 JSP 화면 구성

1) index.jsp

    (1) 시작 페이지
    index.jsp는 jsp:forward를 사용해 answer?command=list로 바로 이동한다.
    프로젝트에 접속하면 글 목록이 먼저 보이도록 만든 구조이다.

2) boardlist.jsp

    (1) 글 목록 화면
    boardlist.jsp는 전체 글 목록을 출력하는 페이지이다.
    Controller가 request에 저장한 list를 JSTL로 반복 출력한다.

    (2) c:choose
    c:choose는 글 목록이 비어 있는 경우와 글이 존재하는 경우를 나누기 위해 사용했다.
    글이 없으면 안내 문구를 출력하고, 글이 있으면 목록을 출력한다.

    (3) c:forEach
    c:forEach는 list에 담긴 AnswerDto 객체들을 하나씩 반복하기 위해 사용했다.
    각 글의 번호, 제목, 작성자, 작성일을 출력한다.

    (4) 답글 들여쓰기
    제목을 출력하기 전에 c:forEach begin="1" end="${dto.titletab}"을 사용해 공백을 출력했다.
    TITLETAB 값이 클수록 제목 앞 공백이 늘어나 답글이 안쪽으로 들어간 것처럼 보인다.

    (5) 상세 링크
    제목을 클릭하면 answer?command=detail&boardno=글번호 요청이 발생한다.
    Controller는 해당 글을 조회해 상세 화면으로 이동시킨다.

3) boardwrite.jsp

    (1) 새 글 작성 화면
    boardwrite.jsp는 새로운 원글을 작성하는 화면이다.
    작성자, 제목, 내용을 입력받아 answer Servlet으로 POST 요청을 보낸다.

    (2) hidden command
    command=boardwrite를 hidden input으로 전달한다.
    Controller는 이 값을 보고 새 글 작성 요청임을 판단한다.

4) boarddetail.jsp

    (1) 상세 보기 화면
    boarddetail.jsp는 글 하나의 상세 정보를 보여주는 화면이다.
    dto에 담긴 글 번호, 작성자, 작성일, 제목, 내용을 EL로 출력한다.

    (2) 답글 버튼
    답글 버튼을 누르면 answer?command=answerform&parentboardno=현재글번호 요청이 발생한다.
    Controller는 부모글 정보를 조회해 답글 작성 화면으로 이동시킨다.

5) answerwrite.jsp

    (1) 답글 작성 화면
    answerwrite.jsp는 특정 글에 대한 답글을 작성하는 화면이다.
    부모글 번호를 hidden input으로 전달한다.

    (2) 제목 기본값
    제목 input에는 RE:${parent.title} 값을 기본으로 넣었다.
    사용자가 어떤 글에 답글을 작성하는지 알 수 있고, 답글 제목임을 표현할 수 있다.

    (3) 취소 버튼
    취소 버튼을 누르면 부모글 상세 페이지로 돌아간다.
    parent.boardno를 이용해 다시 detail 요청을 보낸다.

6) res.jsp

    (1) 결과 처리 화면
    res.jsp는 글 작성이나 답글 작성 결과를 alert로 보여주는 페이지이다.
    msg와 url을 EL로 받아 JavaScript에서 alert를 띄우고 지정된 url로 이동한다.

    (2) 사용하는 이유
    성공과 실패 결과를 공통 화면에서 처리할 수 있다.
    Controller는 메시지와 이동할 URL만 request에 담아 전달하면 된다.

11 오늘 실습의 핵심 정리

1) 답변형 게시판 구조 이해

    (1) 원글과 답글 관계
    답변형 게시판은 원글과 답글의 관계를 유지해야 한다.
    이를 위해 GROUPNO, GROUPSQ, TITLETAB 값을 사용했다.

    (2) 같은 그룹 관리
    원글과 그 답글들은 같은 GROUPNO를 가진다.
    이 값으로 하나의 글 묶음을 만들 수 있다.

    (3) 출력 순서 관리
    GROUPSQ는 같은 그룹 안에서 출력 순서를 결정한다.
    답글이 부모글 바로 아래에 오도록 기존 글들의 GROUPSQ를 조정했다.

    (4) 들여쓰기 관리
    TITLETAB은 답글의 깊이를 화면에 표현하기 위한 값이다.
    JSP에서 TITLETAB 값만큼 공백을 출력해 답글 구조를 시각적으로 보여주었다.

2) 답글 작성 알고리즘

    (1) 부모글 조회
    답글 작성 시 먼저 부모글을 조회한다.
    부모글의 GROUPNO, GROUPSQ, TITLETAB 값을 알아야 답글의 위치를 정할 수 있다.

    (2) 기존 순서 변경
    같은 GROUPNO 안에서 부모글보다 뒤에 있는 글들의 GROUPSQ를 1씩 증가시킨다.
    이는 새 답글이 들어갈 자리를 만드는 과정이다.

    (3) 답글 삽입
    새 답글은 부모글과 같은 GROUPNO를 가진다.
    GROUPSQ는 부모글의 GROUPSQ + 1, TITLETAB은 부모글의 TITLETAB + 3으로 저장된다.

3) 트랜잭션의 중요성

    (1) 여러 작업을 하나로 처리
    답글 작성은 단순 INSERT 하나만 하는 작업이 아니다.
    기존 글들의 순서를 UPDATE한 뒤 새 답글을 INSERT해야 한다.

    (2) commit과 rollback
    답글 INSERT가 성공하면 commit한다.
    실패하면 rollback하여 작업을 취소한다.

    (3) 이유
    순서 변경만 되고 답글 INSERT가 실패하면 게시글 순서가 꼬일 수 있다.
    따라서 답글 작성처럼 여러 SQL이 하나의 흐름으로 묶인 작업에서는 트랜잭션 처리가 중요하다.

4) Servlet MVC 흐름 복습

    (1) Controller
    AnswerController는 command 값을 기준으로 요청을 분기한다.
    사용자의 요청을 받고 Service를 호출한 뒤 알맞은 JSP로 이동시킨다.

    (2) Service
    AnswerService는 Connection을 생성하고 DAO를 호출한다.
    변경 작업의 성공 여부에 따라 commit 또는 rollback을 처리한다.

    (3) DAO
    AnswerDao는 실제 SQL을 실행한다.
    게시글 목록 조회, 새 글 작성, 상세 조회, 답글 순서 변경, 답글 작성을 담당한다.

    (4) DTO
    AnswerDto는 ANSWERBOARD 테이블의 데이터를 Java 객체로 담아 전달한다.
    Controller, Service, DAO, JSP 사이에서 데이터를 이동시키는 역할을 한다.

    (5) View
    JSP는 사용자가 보는 화면을 출력한다.
    EL과 JSTL을 사용해 request에 담긴 데이터를 화면에 표시한다.

5) 오늘 배운 점

    (1) 단순 게시판과 답변형 게시판의 차이
    단순 게시판은 작성 순서대로만 글을 출력하면 되지만, 답변형 게시판은 원글과 답글의 관계를 유지해야 한다.
    그래서 GROUPNO, GROUPSQ, TITLETAB 같은 추가 컬럼이 필요하다는 것을 배웠다.

    (2) 정렬 기준의 중요성
    답변형 게시판에서는 ORDER BY GROUPNO DESC, GROUPSQ가 핵심이다.
    이 정렬 기준을 통해 최신 원글 그룹이 먼저 나오고, 같은 그룹 안에서는 원글과 답글이 순서대로 출력된다.

    (3) 답글 위치 조정
    새 답글을 부모글 바로 아래에 넣기 위해 기존 글들의 GROUPSQ를 먼저 증가시키는 과정이 필요했다.
    단순히 INSERT만 하는 것이 아니라, 기존 데이터의 순서를 조정해야 한다는 점이 중요했다.

    (4) 화면에서 계층 표현
    TITLETAB 값을 이용해 제목 앞에 공백을 출력했다.
    DB에 저장된 숫자 값을 화면의 들여쓰기 표현으로 연결하는 방식이 인상적이었다.

    (5) 실제 게시판 기능에 가까운 구조
    오늘 실습을 통해 일반 게시판보다 더 실제 서비스에 가까운 답변형 게시판 구조를 경험했다.
    원글, 답글, 답글의 답글까지 계층적으로 관리하는 흐름을 이해할 수 있었다.