Spring 답변형 게시판 작성 가이드

메타플랫폼대표
|2024. 7. 23. 10:20
반응형

 

 

2024_0723_SpringBoard

 

GitHub - chaSunil/FirstProject: 1차 프로젝트

1차 프로젝트. Contribute to chaSunil/FirstProject development by creating an account on GitHub.

github.com

 

 

답변형 게시판의 테이블 예시

 

 

 

b-ref => 메인 게시물

b-step => 메인 게시물안에 있는 순서

b_depth => 대댓글

 

 

 

 

소모적인 데이터 관리가 있을 수 있으니, 정규화, 비정규화를 잘 나눠서 DB를 설계한다.

 

 

 

 

사용 이유 :

Interface로 MemberDao 사용자를 위해 작성한다.(인터페이스를 사용안하고 바로 dao를 호출하면 @Autowire가 되어있기에 데이터 사용량이 증가할 수 있어서 필요한 데이터만 사용할 수 있는 인터페이스를 활용해서 객체내의 원하는 Method를 사용한다)

 

 

 

 

context3 dao 변경하기

 

 

sql문 작성하기

 

-- 게시판 일련번호 관리객체
create sequence seq_board_idx



-- 게시판 DB

create table board
(
	b_idx			int,						-- 일련번호
	b_subject		varchar2(200) not null,		-- 제목
	b_content		clob not null,				-- 내용
	b_ip			varchar2(100) not null,		-- 아이피
	b_regdate		date,						-- 작성일자
	b_readhit		int default 0,				-- 조회수
	b_use			char(1) default 'y',		-- 사용유무
	mem_idx			int,						-- 작성자회원번호
	mem_name		varchar2(100),				-- 작성자명
	b_ref			int,						-- 참조글번호
	b_step			int,						-- 글순서
	b_depth			int							-- 글깊이(대댓글)
)

-- 기본키 지정

alter table board
	add constraint pk_board_idx primary key(b_idx);
	
	
-- 외래키 지정
	
alter table board
	add constraint fk_board_mem_idx foreign key(mem_idx)
									references member(mem_idx)
									
select * from member								
-- sample data

-- 새글쓰기

insert into board values (seq_board_idx.nextVal,
							'내가 1등',
							'내가 첫번째로 등록했네',
							'192.168.10.123',
							sysdate,
							0,
							'y',
							11,
							'김관리',
							seq_board_idx.currVal, -- nextVal은 다음번호, currVal은 idx 현재번호
							0,
							0
							)

-- 답글쓰기
							
select * from board

insert into board values (seq_board_idx.nextVal,
							'내가 1.5등',
							'내가 1.5번째로 등록했네',
							'192.168.10.123',
							sysdate,
							0,
							'y',
							11,
							'홍길동',
							1, -- nextVal은 다음번호, currVal은 idx 현재번호
							1,
							1
							)
							

insert into board values (seq_board_idx.nextVal,
							'그래 너가 해라',
							'2번째도 개이득이야',
							'192.168.10.123',
							sysdate,
							0,
							'y',
							11,
							'홍길동',
							1, -- nextVal은 다음번호, currVal은 idx 현재번호
							2,
							2
							)

 

 

 

vo 생성하기

package vo;

public class BoardVo {
	
	int b_idx;
	String b_subject;
	String b_content;
	String b_ip;
	String b_regdate;
	int b_readhit;
	String b_use;
	int mem_idx;
	String mem_name;
	int b_ref;
	int b_step;
	int b_depth;
	
	public int getB_idx() {
		return b_idx;
	}
	public void setB_idx(int b_idx) {
		this.b_idx = b_idx;
	}
	public String getB_subject() {
		return b_subject;
	}
	public void setB_subject(String b_subject) {
		this.b_subject = b_subject;
	}
	public String getB_content() {
		return b_content;
	}
	public void setB_content(String b_content) {
		this.b_content = b_content;
	}
	public String getB_ip() {
		return b_ip;
	}
	public void setB_ip(String b_ip) {
		this.b_ip = b_ip;
	}
	public String getB_regdate() {
		return b_regdate;
	}
	public void setB_regdate(String b_regdate) {
		this.b_regdate = b_regdate;
	}
	public int getB_readhit() {
		return b_readhit;
	}
	public void setB_readhit(int b_readhit) {
		this.b_readhit = b_readhit;
	}
	public String getB_use() {
		return b_use;
	}
	public void setB_use(String b_use) {
		this.b_use = b_use;
	}
	public int getMem_idx() {
		return mem_idx;
	}
	public void setMem_idx(int mem_idx) {
		this.mem_idx = mem_idx;
	}
	public String getMem_name() {
		return mem_name;
	}
	public void setMem_name(String mem_name) {
		this.mem_name = mem_name;
	}
	public int getB_ref() {
		return b_ref;
	}
	public void setB_ref(int b_ref) {
		this.b_ref = b_ref;
	}
	public int getB_step() {
		return b_step;
	}
	public void setB_step(int b_step) {
		this.b_step = b_step;
	}
	public int getB_depth() {
		return b_depth;
	}
	public void setB_depth(int b_depth) {
		this.b_depth = b_depth;
	}
	
}

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "HTTP://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<settings>
		<setting name="cacheEnabled" value="false" />
		<setting name="useGeneratedKeys" value="true" />
		<setting name="defaultExecutorType" value="REUSE" />
	</settings>
	
	<typeAliases>
		<typeAlias type="vo.MemberVo" alias="member"/>
		<typeAlias type="vo.BoardVo" alias="board"/>
	</typeAliases>
	
	<mappers>
		<mapper resource="config/mybatis/mapper/member.xml" />
		<mapper resource="config/mybatis/mapper/board.xml" />
	</mappers>
	
</configuration>

 

 

package dao;

import java.util.List;

import vo.BoardVo;

public interface BoardDao {
	
	List<BoardVo> selectList();

}

 

 

@Repository
public class BoardDaoImpl implements BoardDao {
	
	public BoardDaoImpl() {
		// TODO Auto-generated constructor stub
		
		System.out.println("--BoardDaoImpl--");
	}

	@Autowired
	SqlSession sqlSession;
	
	@Override
	public List<BoardVo> selectList() {
		
		return sqlSession.selectList("board.board_list");
	}
}

 

 

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">

	<select id="board_list" resultType="board">
		select * from board order by b_ref desc, b_step asc
	</select>

</mapper>

 

 

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<!--  CSS/Javascript/Image.... -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<!-- Auto-Detecting -->
	<context:component-scan base-package="dao" />
	<context:component-scan base-package="controller" />
	
	<!-- 수동생성 -->
	
	<!-- Autowired지원속성 : 수동생성시에는 반드시 기록  -->
	<context:annotation-config/>
	
	
	
	
	
	
</beans:beans>

 

 

 

BoardController 생성하기

@Controller
@RequestMapping("/board/")
public class BoardController {

	public BoardController() {
		// TODO Auto-generated constructor stub
		System.out.println("--BoardController()--");
	}
	
	@Autowired
	@Qualifier("board_dao")
	BoardDao board_dao;
	
	@RequestMapping("list.do")
	public String list(Model model) {
		
		// 게시판 목록 가져오기
		List<BoardVo> list = board_dao.selectList();
		
		// DS이 전달해준 Model을 통해서 데이터를 넣는다
		// DS는 model에 저장된 데이터를 request binding시킨다
		model.addAttribute("list",list);
		
		return "board/board_list";
	}
	
}

 

 

 

@Qualifier은 식별자("board_dao") 이름만 가진애만 이걸 사용하겠다.

 

이렇게 같은 Interface를 참조하고 있으면, (implements) Controller에서 지명하고 있는 board_dao는 참조하고 있는 dao중 어떤 dao를 가져올지가 굉장히 애매해진다.

그렇기 때문에 @Qualifier를 사용해서 같은 이름(변수명)을 사용하고 있는 Controller가 이 Dao를 사용할 수 있다고 명시해주면, 명시해준 그 대상만 사용할 수 있다.

 

 

 

board_list.jsp 작성하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>

<style type="text/css">
	@font-face {
	    font-family: 'MabinogiClassicR';
	    src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_2207-01@1.0/MabinogiClassicR.woff2') format('woff2');
	    font-weight: normal;
	    font-style: normal;
	}
	
	* {
		font-family: 'MabinogiClassicR';
	}

	#box {
		width: 1000px;
		margin: auto;
		margin-top: 50px;
	}
	
	#box-img {
		text-align: center;
	}
	
	#title {
		text-align: center;
		font-weight: bold;
		font-size: 26px;
		color: black;
	}
	
	.btn-primary {
		background: black !important;
	}
	
	img {
		width: 200px;
	}
</style>

<script type="text/javascript">
	function insert_form() {
		
		// 글쓰기 변경 확인
		
		// 로그인 여부 체크
		if("${ empty user }" == "true") {
			
			if(confirm("글쓰기는 로그인후 사용가능합니다\n로그인 하시겠습니까?")==false) return;
			
			// 로그인폼으로 이동
			location.href="../member/login_form.do";
			
			return;
		}
		
		// 입력 폼 띄우기
		location.href="insert_form.do"
	}
</script>

</head>
<body>

<div id="box">
	<div id="box-img">
		<img src="https://i.ibb.co/m8Q3fNX/image.webp" width=200>
	</div>
		<h3 id="title">게시판</h3>
	
	<div class="row" style="margin-top: 30px; margin-bottom: 5px;">
		<div class="col-sm-4">
			<input class="btn btn-primary" type="button" value="글쓰기"
					onclick="insert_form();">
		</div>
		<div class="col-sm-8" style="text-align: right;">
			<!-- 로그인이 안된경우 -->
			<c:if test="${ empty sessionScope.user }">
				<input class="btn btn-primary" type="button" value="로그인"
						onclick="location.href='../member/login_form.do'">
			</c:if>
			<!-- 로그인이 된경우 -->
			<c:if test="${ not empty sessionScope.user }">
				<p>${ user.mem_name }님 환영합니다.</p>
				<input class="btn btn-primary" type="button" value="로그아웃"
						onclick="location.href='../member/logout.do'">
			</c:if>
		</div>
	</div>
	
	<table class="table">
		<tr style="color: white; background-color: black;">
			<th>번호</th>
			<th style="width:50%;">제목</th>
			<th>작성자</th>
			<th>작성일자</th>
			<th>조회수</th>
		</tr>
		
		<!-- 데이터가 없는 경우 -->
		<c:if test="${ empty list }">
			<tr>
				<td colspan="5" align="center">
					<font color="red">게시물이 없습니다.</font>
				</td>
			</tr>
		</c:if>
		
		<!-- 데이터가 있는 경우 -->
		<c:forEach var="vo" items="${ requestScope.list }">
			<tr>
				<td>${ vo.b_idx }</td>
				<td>
					<!-- 답글이면 b_depth만큼 들여쓰기 -->
					<c:forEach begin="1" end="${ vo.b_depth }">
						&nbsp;&nbsp;&nbsp;
					</c:forEach>
					<c:if test="${ vo.b_depth ne 0 }">
					ㄴ
					</c:if>
				<span class="b_subject"><a href="view.do?b_idx=${ vo.b_idx }">${ vo.b_subject }</a></span></td>
				<td>${ vo.mem_name }</td>
				<td>${ vo.b_regdate }</td>
				<td>${ vo.b_readhit }</td>
			</tr>
		</c:forEach>
		
	</table>
	
</div>




</body>
</html>

 

 

 

insert_form 작성하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>

<style type="text/css">
	@font-face {
	    font-family: 'MabinogiClassicR';
	    src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_2207-01@1.0/MabinogiClassicR.woff2') format('woff2');
	    font-weight: normal;
	    font-style: normal;
	}
	
	* {
		font-family: 'MabinogiClassicR';
	}
	
	#box-img {
		text-align: center;
	}

	#box {
		width: 600px;
		margin: auto;
	}
	
	textarea {
		resize: none;
	}
	
	h4 {
		font-weight: bold;
	}
	
	.btn {
		background-color: black !important;
	}
</style>

<script type="text/javascript">

	function send(f) {
		
		let b_subject = f.b_subject.value.trim();
		let b_content = f.b_content.value.trim();
		
		if(b_subject=='') {
			alert("제목을 입력하세요!!");
			f.b_subject.value="";
			f.b_subject.focus();
			return;
		}
		
		if(b_content=='') {
			alert("제목을 입력하세요!!");
			f.b_content.value="";
			f.b_content.focus();
			return;
		}
		
		f.action = "insert.do";
		f.submit(); // 
		
	}
	
</script>
</head>
<body>

<form>

	<div id="box-img">
		<img src="https://i.ibb.co/m8Q3fNX/image.webp" width=200>
	</div>
	
	<div id="box">
		<div class="panel panel-primary">
		    <div class="panel-heading" style="background-color: black !important;">
		    	<h4>새글쓰기</h4>
		    </div>
		    <div class="panel-body">
		    	<div>
		    		<h4>제목 : </h4>
		    		<input class="form-control" name="b_subject">
		    	</div>
		    	<div>
		    		<h4>내용 : </h4>
		    		<textarea class="form-control" rows="10" name="b_content"></textarea>
		    	</div>
		    	
		    	<div style="margin-top: 10px;">
		    		<input class="btn btn-info" type="button" value="목록보기" 
		    				onclick="location.href='list.do'">
		    		<input class="btn btn-success" type="button" value="글올리기"
		    				onclick="send(this.form);">
		    	</div>
		    	
		    </div>
  		</div>
	</div>
</form>

</body>
</html>

 

 

 

 

Controllert 추가하기

@Controller
@RequestMapping("/board/")
public class BoardController {

	public BoardController() {
		// TODO Auto-generated constructor stub
		System.out.println("--BoardController()--");
	}
	
	@Autowired
	HttpServletRequest request;
	
	@Autowired
	HttpSession session;
	
	@Autowired
	@Qualifier("board_dao")
	BoardDao board_dao;
	
	@RequestMapping("list.do")
	public String list(Model model) {
		
		// 게시판 목록 가져오기
		List<BoardVo> list = board_dao.selectList();
		
		// DS이 전달해준 Model을 통해서 데이터를 넣는다
		// DS는 model에 저장된 데이터를 request binding시킨다
		model.addAttribute("list",list);
		
		return "board/board_list";
	}
	
	@RequestMapping("insert_form.do")
	public String insert_form() {
		
		return "board/board_insert_form";
	}
	
	// /board/insert.do?b_subject=제목&b_content=내용
	@RequestMapping("insert.do")
	public String insert(BoardVo vo, RedirectAttributes ra) {
		
		// 사용자정보 가져오기
		MemberVo user = (MemberVo) session.getAttribute("user");
		
		if(user==null) {
			
			ra.addAttribute("reason","session_timeout");
			
			return "redirect:../member/login_form.do";
		}
		
		// 사용자정보 vo에 등록
		vo.setMem_idx(user.getMem_idx());
		vo.setMem_name(user.getMem_name());
		
		// 작성자 ip
		String b_ip = request.getRemoteAddr();
		vo.setB_ip(b_ip);
		
		String b_content = vo.getB_content().replaceAll("\n", "<br>");
		
		vo.setB_content(b_content);
		
		return "redirect:list.do";
	}
	
}

 

 

 

 

RedirectAttributes로 parameter 값을 전달해준다.

 

 

 

 

dao 추가하기

public interface BoardDao {
	
	List<BoardVo> selectList();
	
	int insert(BoardVo vo);

}
	@Override
	public int insert(BoardVo vo) {
		
		return sqlSession.insert("board.board_insert",vo);
	}

 

 

 

 

xml 추가하기

	<!-- 새글쓰기 -->
	<insert id="board_insert" parameterType="vo.BoardVo">
		insert into board values (seq_board_idx.nextVal,
							#{ b_subject },
							#{ b_context },
							#{ b_ip },
							sysdate,
							0,
							'y',
							#{ mem_idx },
							#{ mem_name },
							seq_board_idx.currVal,
							0,
							0
							)
	</insert>

 

 

 

view.do Controller에 작성하기

	// 상세보기
	// /board/view.do?b_idx=5
	@RequestMapping("view.do")
	public String view(int b_idx, Model model) {
		
		// b_idx에 해당되는 게시물 1건
		BoardVo vo = board_dao.selectOne(b_idx);
		
		// 결과적으로 request binding
		model.addAttribute("vo",vo);
		
		return "board/board_view";
	}

 

 

 

 

view.jsp 작성하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>

<style type="text/css">
	@font-face {
	    font-family: 'MabinogiClassicR';
	    src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_2207-01@1.0/MabinogiClassicR.woff2') format('woff2');
	    font-weight: normal;
	    font-style: normal;
	}
	
	* {
		font-family: 'MabinogiClassicR';
	}
	
	#box-img {
		text-align: center;
	}
	
	#box-img > img {
		width: 200px;
	}

	#box {
		width: 800px;
		margin: auto;
		margin-top: 20px;
	}
	
	.common {
		border: 1px solid #eeeeee;
		padding: 5px;
		margin-bottom: 5px;
	}
	
	.content {
		min-height: 200px;
	}
		
	.btn {
		background-color: black !important;
	}

</style>
</head>
<body>

	<div id="box-img">
		<img src="https://i.ibb.co/m8Q3fNX/image.webp" width=200>
	</div>

	<div id="box">
		<div class="panel panel-primary">
      	<div class="panel-heading" style="background-color: black !important;">
      	<h4><b>${ vo.mem_name }</b>님의 글</h4>
      	</div>
      	<div class="panel-body">
      		<div class="common subject">${ vo.b_subject }</div>
      		<div class="common content">${ vo.b_content }</div>
      		<div class="common regdate">${ vo.b_regdate }(${ vo.b_ip })</div>
      		
      		<div>
      			<input class="btn btn-primary" type="button" value="목록보기"
      					onclick="location.href='list.do'">
      			<!-- 로그인 상태에서만 확인 가능  -->
      			<c:if test="${ not empty user }">
      				<input class="btn btn-primary" type="button" value="답글쓰기">
      			</c:if>
      			
      			<!-- 글의 작성자만 수정/삭제 가능 -->
      			<c:if test="${ vo.mem_idx == user.mem_idx }">
	      			<input class="btn btn-primary" type="button" value="수정하기">
      				<input class="btn btn-primary" type="button" value="삭제하기">
      			</c:if>
      		</div>
      	</div>
    </div>
	</div>

</body>
</html>

 

 

 

 

조회수 증가 로직 생성

	// 상세보기
	// /board/view.do?b_idx=5
	@RequestMapping("view.do")
	public String view(int b_idx, Model model) {
		
		// b_idx에 해당되는 게시물 1건
		BoardVo vo = board_dao.selectOne(b_idx);
		
		// 조회수 증가
		int res = board_dao.update_readhit(b_idx);
		
		// 결과적으로 request binding
		model.addAttribute("vo",vo);
		
		return "board/board_view";
	}

 

 

 

 

Dao 생성하기

	@Override
	public int update_readhit(int b_idx) {
		// TODO Auto-generated method stub
		return sqlSession.update("board.board_update_readhit",b_idx);
	}
int update_readhit(int b_idx);

 

 

 

 

xml 생성하기

	<update id="board_update_readhit" parameterType="int">
		update board set b_readhit = b_readhit +1 where b_idx=#{ b_idx } 
	</update>

 

 

 

 

controller view 조회수 session받아와서 1만 올라가게끔 로직수정

	// 상세보기
	// /board/view.do?b_idx=5
	@RequestMapping("view.do")
	public String view(int b_idx, Model model) {
		
		// b_idx에 해당되는 게시물 1건
		BoardVo vo = board_dao.selectOne(b_idx);
		
		// 조회수 증가(F5 새로고침하면 올라가는거 방지 로직)
		if(session.getAttribute("show")==null) {
			
			// 조회수 증가
			int res = board_dao.update_readhit(b_idx);
			// 뒤에 true던, false던 상관없음
			session.setAttribute("show", true);
		}
		
		
		// 결과적으로 request binding
		model.addAttribute("vo",vo);
		
		return "board/board_view";
	}
	@RequestMapping("list.do")
	public String list(Model model) {
		
		// session에 기록되어있는 show를 삭제
		session.removeAttribute("show");
		
		// 게시판 목록 가져오기
		List<BoardVo> list = board_dao.selectList();
		
		// DS이 전달해준 Model을 통해서 데이터를 넣는다
		// DS는 model에 저장된 데이터를 request binding시킨다
		model.addAttribute("list",list);
		
		return "board/board_list";
	}

 

 

 

 

텍스트가 길어지면 ..으로 생략해서 표현하기

	.b_subject {
		width: 120px;
		display: block;
		/* ellipsis 텍스트 너무 길어지면 ..으로 변경 */
		overflow: hidden;
		white-space: nowrap;
		text-overflow: ellipsis;
		word-break: break-all;
	}
반응형