CSS & BootStrap
align-items-start : 위 정렬
align-items-center : 가운데 정렬
align-items-end : 아래 정렬
align-items-stretch : 늘리기 = default 값
<div class="container">
<h1>column 의 정렬</h1>
<div class="row align-items-start">
<div class="col">1</div>
<div class="col">2</div>
</div>
<div class="row align-items-center">
<div class="col">1</div>
<div class="col">2</div>
</div>
<div class="row align-items-end">
<div class="col">1</div>
<div class="col">2</div>
</div>
</div>
⭐ 자식의 정렬을 부모가 결정
align-self-start
align-self-center
align-self-end
justify-content-start : 좌측 정렬
justify-content-center : 가운데 정렬
justify-content-end : 우측 정렬
justify-content-around :어떤 칼럼의 주변을 똑같이 설정
justify-content-between : 칼럼의 사이를 공간을 똑같이 설정
justify-content-evenly : 주변의 공간을 다 똑같이 설정
⭐ 부모가 자식의 수평 정렬 지정 가능
<div class="container">
<h1>Card Component 사용해 보기</h1>
<div class="row">
<div class="col-3">
<div class="card">
<img class="card-img-top" src="images/1.jpg"/>
<div class="card-body">
<h3 class="card-title">제목</h3>
<p class="card-text">Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quidem iusto quos animi, quaerat tempora inventore similique, ipsam quisquam repudiandae voluptatibus perferendis vel alias vero hic ipsum recusandae labore!
Perferendis, nulla.</p>
<a href="#" class="btn btn-success">자세히 보기</a>
</div>
</div>
</div>
</div>
</div>
.card-body img{
width:50px;
height:50px;
}
<div class="container">
<h1>댓글 출력 레이아웃 sample</h1>
<div class="card mb-3">
<div class="card-body d-flex">
<img class="rounded-circle me-3" src="images/1.jpg" alt="프로필 이미지">
<div class="flex-grow-1">
<div class="d-flex justify-content-between">
<div>
<strong>작성자: 유재석</strong>
<span>수신자: 전체</span>
</div>
<small>25.07.25 12:00</small>
</div>
<pre>이건 첫번째 댓글입니다</pre>
<button class="btn btn-sm btn-outline-primary show-reply-btn">댓글</button>
<!-- 댓글 입력 폼 (처음에는 숨김)-->
<div class="d-none form-div">
<form action="comment-save.jsp" method="post">
<textarea class="form-control mb-2" rows="2" placeholder="댓글을 입력하세요"></textarea>
<button type="submit" class="btn btn-sm btn-success">등록</button>
<button type="reset" class="btn btn-sm btn-secondary cancel-reply-btn">취소</button>
</form>
</div>
</div>
</div>
</div>
</div>
// 모든 댓글 버튼에 이벤트 등록
document.querySelectorAll(".show-reply-btn").forEach(item=>{
// 매개변수에 전달된 item 은 댓글 button 의 참조값이다
item.addEventListener("click", ()=>{
// 클릭한 버튼의 다음 형제 요소의 class 모곡에서 d-none 을 제거
item.nextElementSibling.classList.remove("d-none");
// 클릭한 버튼의 class 목록에 d-none 을 추가
item.classList.add("d-none");
});
});
document.querySelectorAll(".cancel-reply-btn").forEach(item=>{
item.addEventListener("click", ()=>{
// 가장 가까운 부모 요소 중에 클래스 속성이 form-div 인 요소
const formDiv = item.closest(".form-div");
// formDiv 에 d-none 클래스를 추가해서 안 보이게 하고
formDiv.classList.add("d-none");
// formDiv 의 이전 형제 요소(댓글 버튼)에 d-none 클래스를 제거해서 보이게 한다
// previousElementSibling : 이전 형제 요소
formDiv.previousElementSibling.classList.remove("d-none");
});
});
JAVA & JSP
<div class="card my-3">
<div class="card-header bg-primary-subtle text-white">
댓글을 입력해 주세요
</div>
<div class="card-body">
<!-- 원글의 댓글을 작성할 폼 -->
<form action="save-comment.jsp" method="post">
<!-- 숨겨진 입력값 -->
<input type="hidden" name="parentNum" value="<%=dto.getNum() %>"/>
<input type="hidden" name="targetWriter" value="<%=dto.getWriter() %>" />
<div class="mb-3">
<label for="commentContent" class="form-label">댓글 내용</label>
<textarea id="commentContent" name="content" rows="5" class="form-control" placeholder="댓글을 입력하세요"></textarea>
</div>
<button type="submit" class="btn bg-primary-subtle text-white">등록</button>
</form>
</div>
</div>
public class CommentDto {
private int num;
private String writer;
private String content;
private String targetWriter;
private int groupNum;
private int parentNum;
private String deleted;
private String createdAt;
// setter, getter
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getTargetWriter() {
return targetWriter;
}
public void setTargetWriter(String targetWriter) {
this.targetWriter = targetWriter;
}
public int getGroupNum() {
return groupNum;
}
public void setGroupNum(int groupNum) {
this.groupNum = groupNum;
}
public int getParentNum() {
return parentNum;
}
public void setParentNum(int parentNum) {
this.parentNum = parentNum;
}
public String getDeleted() {
return deleted;
}
public void setDeleted(String deleted) {
this.deleted = deleted;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
}
public class CommentDao {
private static CommentDao dao;
static {
dao = new CommentDao();
}
// 생성자를 private 로 해서 외부에서 객체 생성하지 못하도록
private CommentDao() {}
// 자신의 참조값을 리턴해주는 static 메소드를 제공한다
public static CommentDao getInstance() {
return dao;
}
{
public int getSequence() {
int num = 0;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = new DbcpBean().getConn();
String sql = """
SELECT comments_seq.NEXTVAL AS num FROM DUAL
""";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if (rs.next()) {
num = rs.getInt("num");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (Exception e) {
}
}
return num;
}
public boolean insert(CommentDto dto) {
Connection conn = null;
PreparedStatement pstmt = null;
int rowCount = 0;
try {
conn = new DbcpBean().getConn();
String sql = """
INSERT INTO comments
(num, writer, targetWriter, content, parentNum, groupNum)
VALUES(?, ?, ?, ?, ?, ?)
""";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, dto.getNum());
pstmt.setString(2, dto.getWriter());
pstmt.setString(3, dto.getTargetWriter());
pstmt.setString(4, dto.getContent());
pstmt.setInt(5, dto.getParentNum());
pstmt.setInt(6, dto.getGroupNum());
rowCount = pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (Exception e) {
}
}
}
<%
// 폼 전송되는 댓글 정보를 얻어낸다
int parentNum = Integer.parseInt(request.getParameter("parentNum"));
String targetWriter = request.getParameter("targetWriter");
String content = request.getParameter("content");
// 댓글 작성자 정보는 session 으로부터 얻어낸다
String writer = (String) session.getAttribute("userName");
// 저장할 댓글의 글 번호를 미리 얻어낸다
int num = CommentDao.getInstance().getSequence();
// 저장할 댓글 정보를 dto 에 담고
CommentDto dto = new CommentDto();
dto.setNum(num);
dto.setWriter(writer);
dto.setTargetWriter(targetWriter);
dto.setContent(content);
dto.setParentNum(parentNum);
dto.setGroupNum(num); // 원글의 댓글은 자신의 글번ㄴ호가 댓글의 그룹번호
// DB 에 저장하고
boolean isSuccess = CommentDao.getInstance().insert(dto);
%>
private String profileImage;
public String getProfileImage() {
return profileImage;
}
public void setProfileImage(String profileImage) {
this.profileImage = profileImage;
}
public List<CommentDto> selectList(int parentNum) {
List<CommentDto> list = new ArrayList<>();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = new DbcpBean().getConn();
String sql = """
SELECT num, writer, targetWriter, content, deleted, groupNum,
createdAt, profileImage
FROM comments
INNER JOIN users ON comments.writer = users.userName
WHERE parentNum = ?
ORDER BY groupNum DESC, num ASC
""";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, parentNum);
rs = pstmt.executeQuery();
while (rs.next()) {
CommentDto dto = new CommentDto();
dto.setNum(rs.getInt("num"));
dto.setWriter(rs.getString("writer"));
dto.setTargetWriter(rs.getString("targetWriter"));
dto.setContent(rs.getString("content"));
dto.setParentNum(parentNum);
dto.setGroupNum(rs.getInt("groupNum"));
dto.setDeleted(rs.getString("deleted"));
dto.setCreatedAt(rs.getString("createdAt"));
dto.setProfileImage(rs.getString("profileImage"));
list.add(dto);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (Exception e) {
}
}
return list;
}
<%
// 댓글 목록 DB 에서 읽어오기
List<CommentDto> commentList = CommentDao.getInstance().selectList(num);
%>