일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- gcloud.gabia
- centos
- 게시판
- tomcat
- server
- packet-tracer
- 패킷트레이서
- configration
- java
- board
- 언어
- 무농약
- 이클립스
- 설정
- eclipse
- interface
- JSP
- 한글
- CLI
- servlet
- vmware
- 홈가든
- cisco
- centos7
- war
- nppFTP
- 서버
- 설치
- comand
- 시스코
- Today
- Total
넓고얕은지식사전
JSP & Servlet 게시판 구현하기(가입) 본문
이클립스에서 회원제 게시판을 구현해 보자
게시판을 구현하기에 앞서 DB를 구축하자.
필자는 앞서 만들어 놓은 VMware DB서버를 사용할 예정이다. 이 글이 처음 본 사람 이라면 아래의 링크를 통해 먼저 DB서버를 구축해 보도록 하자.
https://nullgoyatten.tistory.com/6
DB서버의 IP는 192.168.10.90으로 잡아두었다.
이제 heidiSQL로 접속해서 아래 DB생성 쿼리를 실행시켜보자
-- DB 생성
CREATE DATABASE board DEFAULT character SET UTF8;
-- DB사용자 생성
CREATE USER 'jspexam'@'localhost' IDENTIFIED BY 'jsppw';
CREATE USER 'jspexam'@'%' IDENTIFIED BY 'jsppw';
--board DB 권한 열기
GRANT ALL PRIVILEGES ON board.* TO 'jspexam'@'localhost';
GRANT ALL PRIVILEGES ON board.* TO 'jspexam'@'%';
-- member 테이블 생성
CREATE TABLE bomemberard.member(
memberid VARCHAR(50) PRIMARY KEY,
name VARCHAR(50) NOT NULL,
password VARCHAR(10) NOT NULL,
regdate DATETIME NOT NULL
) ENGINE=INNODB DEFAULT CHARACTER SET = UTF8;
생성이 잘 되었다.
이제 이클립스로 돌아와서 코드를 짜보자.
1.프로젝트 생성(프로젝트명 : board)
이때 바로 Finish누르지 말고 next 눌러서 web.xml 생성하는 란에 체크를 하도록 하자.
2. WebContent > WEB-INF > lib 경로에 아래 jar파일 복사해 넣기
3. 패키지 생성 (패키지명 : jdbc)
4. DB 연동을 하므로 커넥션 관련 코드를 작성해야 한다. (커넥션 풀을 초기화 하기 위해서)
경로 : jdbc > DBCPInitListener.java
package jdbc;
import java.io.IOException;
import java.io.StringReader;
import java.sql.DriverManager;
import java.util.Properties;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDriver;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
public class DBCPInitListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
String poolConfig =
sce.getServletContext().getInitParameter("poolConfig");
Properties prop = new Properties();
try {
prop.load(new StringReader(poolConfig));
} catch (IOException e) { e.printStackTrace();
throw new RuntimeException("config load fail", e);
}
loadJDBCDriver(prop);
initConnectionPool(prop);
}
private void loadJDBCDriver(Properties prop) {
String driverClass = prop.getProperty("jdbcDriver");
try {
Class.forName(driverClass);
} catch (ClassNotFoundException ex) { ex.printStackTrace();
throw new RuntimeException("fail to load JDBC Driver", ex);
}
}
private void initConnectionPool(Properties prop) {
try {
String jdbcUrl = prop.getProperty("jdbcUrl");
String username = prop.getProperty("dbUser");
String pw = prop.getProperty("dbPass");
ConnectionFactory connFactory =
new DriverManagerConnectionFactory(jdbcUrl, username, pw);
PoolableConnectionFactory poolableConnFactory =
new PoolableConnectionFactory(connFactory, null);
String validationQuery = prop.getProperty("validationQuery");
if (validationQuery != null && !validationQuery.isEmpty()) {
poolableConnFactory.setValidationQuery(validationQuery);
}
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setTimeBetweenEvictionRunsMillis(1000 * 60 * 5);
poolConfig.setTestWhileIdle(true);
int minIdle = getIntProperty(prop, "minIdle", 5);
poolConfig.setMinIdle(minIdle);
int maxTotal = getIntProperty(prop, "maxTotal", 50);
poolConfig.setMaxTotal(maxTotal);
GenericObjectPool<PoolableConnection> connectionPool =
new GenericObjectPool<>(poolableConnFactory,poolConfig);
poolableConnFactory.setPool(connectionPool);
Class.forName("org.apache.commons.dbcp2.PoolingDriver");
PoolingDriver driver = (PoolingDriver)
DriverManager.getDriver("jdbc:apache:commons:dbcp:");
String poolName = prop.getProperty("poolName");
driver.registerPool(poolName, connectionPool);
} catch (Exception e) { e.printStackTrace();
throw new RuntimeException(e);
}
}
private int getIntProperty(Properties prop, String propName, int defaultValue) {
String value = prop.getProperty(propName);
if (value == null) {return defaultValue;}
return Integer.parseInt(value);
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
}
}
5. web.xml 수정
DBCPInitListener는 서블릿 컨텍스트 리스너이므로 web.xml(경로 : WebContent > WEB-INF)에 아래 설정을 추가해줘야 한다.
<!-- DBCPInitListener 관련설정 -->
<listener>
<listener-class>jdbc.DBCPInitListener</listener-class>
</listener>
<context-param>
<param-name>poolConfig</param-name>
<param-value>
jdbcDriver=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://192.168.10.90:3306/board?serverTimezone=Asia/Seoul
dbUser=jspexam
dbPass=jsppw
validationQuery=SELET 1
minIdle=3
maxTotal=30
poolName=board
</param-value>
</context-param>
6. 패키지 생성 (패키지명 : jdbc.connection)
7. 커넥션을 구할 때 사용할 ConnectionProvider 작성
JDBC URL을 보면 web.xml에서 지정한 poolName 값인 board를 풀 이름으로 사용했다.
경로 : jdbc.connection > ConnectionProvider.java
package jdbc.connection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionProvider {
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(
"jdbc:apache:commons:dbcp:board");
}
}
8. DB커넥션 테스트
경로 : WebContent > dbconnTEST.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="jdbc.connection.ConnectionProvider"%>
<%@page import="java.sql.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>커넥션 테스트</title>
</head>
<body>
<%
try (Connection conn = ConnectionProvider .getConnection()) {
out.println("커넥션 연결 성공");
} catch (SQLException ex) {
out.println("커넥션 연결 실패 : " + ex.getMessage());
application.log("커넥션 연결 실패", ex);
}
%>
</body>
</html>
여기서 잠깐, 처음으로 프로젝트를 만들면 아래와 같은 메세지가 뜨는 경우가 있다.
이때는 프로젝트 우클릭 > Build Path > Configure Build Path... > Libraries > Add Library... > Server Runtime > Next > Apache Tomcat v8.5 > Finish > Apply and Close
이러면 해결된다.(아래 캡처를 참고하자.)
이제 dbconnTEST.jsp를 실행해서 커넥션 연결 테스트를 해보자.
9. 커넥션 관련 코드 작성을 편리하게 하도록 하는 JdbcUtil도 작성해준다.
경로 : jdbc > JdbcUtil.java
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtil {
public static void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException ex) {
}
}
}
public static void close(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ex) {
}
}
}
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
}
}
}
public static void rollback(Connection conn) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
}
}
}
}
10. 패키지 생성 (패키지명 : util)
11. 문자 인코딩 필터 설정 작성 CharacterEncodingFilter
경로 : util > CharacterEncodingFilter.java
package util;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class CharacterEncodingFilter implements Filter {
private String encoding;
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
req.setCharacterEncoding(encoding);
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig config) throws ServletException {
encoding = config.getInitParameter("encoding");
if (encoding == null) {
encoding = "UTF-8";
}
}
@Override
public void destroy() {
}
}
12. web.xml 수정
web.xml(경로 : WebContent > WEB-INF)에 아래와 같이 CharacterEncodingFilter 설정을 추가해줘야 한다.
<!-- CharacterEncodingFilter 관련설정 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>util.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
13. 패키지 생성 (패키지명 : mvc.command)
14.CommandHandler 작성
경로 : mvc.command > CommandHandler.java
package mvc.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface CommandHandler {
public String process(HttpServletRequest req, HttpServletResponse res)
throws Exception;
}
15.NullHandler 작성
경로 : mvc.command > NullHandler.java
package mvc.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class NullHandler implements CommandHandler {
@Override
public String process(HttpServletRequest req, HttpServletResponse res)
throws Exception {
res.sendError(HttpServletResponse.SC_NOT_FOUND);
return null;
}
}
16.패키지 생성 (패키지명 : mvc.controller)
17.ControllerUsingURI 작성
경로 : mvc.command > ControllerUsingURI.java
package mvc.controller;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import mvc.command.CommandHandler;
import mvc.command.NullHandler;
public class ControllerUsingURI extends HttpServlet {
// <커맨드, 핸들러인스턴스> 매핑 정보 저장
private Map<String, CommandHandler> commandHandlerMap =
new HashMap<>();
public void init() throws ServletException {
String configFile = getInitParameter("configFile");
Properties prop = new Properties();
String configFilePath = getServletContext().getRealPath(configFile);
try (FileReader fis = new FileReader(configFilePath)) {
prop.load(fis);
} catch (IOException e) {
throw new ServletException(e);
}
Iterator keyIter = prop.keySet().iterator();
while (keyIter.hasNext()) {
String command = (String) keyIter.next();
String handlerClassName = prop.getProperty(command);
try {
Class<?> handlerClass = Class.forName(handlerClassName);
CommandHandler handlerInstance =
(CommandHandler) handlerClass.newInstance();
commandHandlerMap.put(command, handlerInstance);
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException e) {
throw new ServletException(e);
}
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
process(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
process(request, response);
}
private void process(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String command = request.getRequestURI();
if (command.indexOf(request.getContextPath()) == 0) {
command = command.substring(request.getContextPath().length());
}
CommandHandler handler = commandHandlerMap.get(command);
if (handler == null) {
handler = new NullHandler();
}
String viewPage = null;
try {
viewPage = handler.process(request, response);
} catch (Throwable e) {
throw new ServletException(e);
}
if (viewPage != null) {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage);
dispatcher.forward(request, response);
}
}
}
18. web.xml 수정
web.xml(경로 : WebContent > WEB-INF)에 아래와 같이 ControllerUsingURI를 위한 설정을 추가해줘야 한다.
<!-- ControllerUsingURI 관련설정 -->
<servlet>
<servlet-name>ControllerUsingURI</servlet-name>
<servlet-class>mvc.controller.ControllerUsingURI</servlet-class>
<init-param>
<param-name>configFile</param-name>
<param-value>
/WEB-INF/commandHandlerURI.properties
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ControllerUsingURI</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
19.commandHandlerURI.properties 파일 생성
아직까지 구현된 핸들러 코드는 없으므로 빈 파일로 작성
경로 : WebContent > WEB-INF > commandHandlerURI.properties
# comment
20.패키지 생성 (패키지명 : member.model)
21. member 테이블의 데이터를 담을 때 사용할 Member 클래스 작성
경로 : member.model > Member.java
package member.model;
import java.util.Date;
public class Member {
private String id;
private String name;
private String password;
private Date regDate;
public Member(String id, String name, String password, Date regDate) {
this.id = id;
this.name = name;
this.password = password;
this.regDate = regDate;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public Date getRegDate() {
return regDate;
}
public boolean matchPassword(String pwd) {
return password.equals(pwd);
}
}
22.패키지 생성 (패키지명 : member.dao)
23. member 테이블에 데이터를 입력할 때 사용할 MemberDao 클래스 작성
경로 : member.dao > MemberDao.java
package member.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import jdbc.JdbcUtil;
import member.model.Member;
public class MemberDao {
public Member selectById(Connection conn, String id) throws SQLException {
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = conn.prepareStatement(
"select * from member where memberid = ?");
pstmt.setString(1, id);
rs = pstmt.executeQuery();
Member member = null;
if (rs.next()) {
member = new Member(
rs.getString("memberid"),
rs.getString("name"),
rs.getString("password"),
toDate(rs.getTimestamp("regdate")));
}
return member;
} finally {
JdbcUtil.close(rs);
JdbcUtil.close(pstmt);
}
}
private Date toDate(Timestamp date) {
return date == null ? null : new Date(date.getTime());
}
public void insert(Connection conn, Member mem) throws SQLException {
try (PreparedStatement pstmt =
conn.prepareStatement("insert into member values(?,?,?,?)")) {
pstmt.setString(1, mem.getId());
pstmt.setString(2, mem.getName());
pstmt.setString(3, mem.getPassword());
pstmt.setTimestamp(4, new Timestamp(mem.getRegDate().getTime()));
pstmt.executeUpdate();
}
}
}
24.패키지 생성 (패키지명 : member.service)
25. MemberDao를 이용해서 실제로 회원 가입 기능을 처리하는 코드를 만들어 보자. 먼저 만들 코드는 JoinRequest 클래스이다. JoinRequest 클래스는 JoinService가 회원 가입 기능을 구현할 때 필요한 요청 데이터를 담는 클래스로서 다음과 같다.
경로 : member.service > JoinRequest.java
package member.service;
import java.util.Map;
public class JoinRequest {
private String id;
private String name;
private String password;
private String confirmPassword;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
public boolean isPasswordEqualToConfirm() {
return password != null && password.equals(confirmPassword);
}
public void validate(Map<String, Boolean> errors) {
checkEmpty(errors, id, "id");
checkEmpty(errors, name, "name");
checkEmpty(errors, password, "password");
checkEmpty(errors, confirmPassword, "confirmPassword");
if (!errors.containsKey("confirmPassword")) {
if (!isPasswordEqualToConfirm()) {
errors.put("notMatch", Boolean.TRUE);
}
}
}
private void checkEmpty(Map<String, Boolean> errors,
String value, String fieldName) {
if (value == null || value.isEmpty())
errors.put(fieldName, Boolean.TRUE);
}
}
26. DuplicateIdException.
경로 : member.service > DuplicateIdException.java
package member.service;
public class DuplicateIdException extends RuntimeException {
}
27. JoinService.
경로 : member.service > JoinService.java
package member.service;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import jdbc.JdbcUtil;
import jdbc.connection.ConnectionProvider;
import member.dao.MemberDao;
import member.model.Member;
public class JoinService {
private MemberDao memberDao = new MemberDao();
public void join(JoinRequest joinReq) {
Connection conn = null;
try {
conn = ConnectionProvider.getConnection();
conn.setAutoCommit(false);
Member member = memberDao.selectById(conn, joinReq.getId());
if (member != null) {
JdbcUtil.rollback(conn);
throw new DuplicateIdException();
}
memberDao.insert(conn,
new Member(
joinReq.getId(),
joinReq.getName(),
joinReq.getPassword(),
new Date())
);
conn.commit();
} catch (SQLException e) {
JdbcUtil.rollback(conn);
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn);
}
}
}
28.패키지 생성 (패키지명 : member.command)
29. JoinHandler 구현
경로 : member.command > JoinHandler.java
JoinHandler는 다음과 같이 구현한다.
- Get 방식으로 요청이 오면 폼을 보여주는 뷰인 joinForm.jsp를 리턴한다.
- Post 방식으로 요청이 오면 회원 가입을 처리하고 결과를 보여주는 뷰를 리턴한다.
-입력 데이터가 잘못될 경우 다시 joinForm.jsp를 뷰로 리턴한다.
-회원 가입에 성공한 경우 joinSuccess.jsp를 뷰로 리턴한다.
package member.command;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import member.service.DuplicateIdException;
import member.service.JoinRequest;
import member.service.JoinService;
import mvc.command.CommandHandler;
public class JoinHandler implements CommandHandler {
private static final String FORM_VIEW = "/WEB-INF/view/joinForm.jsp";
private JoinService joinService = new JoinService();
@Override
public String process(HttpServletRequest req, HttpServletResponse res) {
if (req.getMethod().equalsIgnoreCase("GET")) {
return processForm(req, res);
} else if (req.getMethod().equalsIgnoreCase("POST")) {
return processSubmit(req, res);
} else {
res.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return null;
}
}
private String processForm(HttpServletRequest req, HttpServletResponse res) {
return FORM_VIEW;
}
private String processSubmit(HttpServletRequest req, HttpServletResponse res) {
JoinRequest joinReq = new JoinRequest();
joinReq.setId(req.getParameter("id"));
joinReq.setName(req.getParameter("name"));
joinReq.setPassword(req.getParameter("password"));
joinReq.setConfirmPassword(req.getParameter("confirmPassword"));
Map<String, Boolean> errors = new HashMap<>();
req.setAttribute("errors", errors);
joinReq.validate(errors);
if (!errors.isEmpty()) {
return FORM_VIEW;
}
try {
joinService.join(joinReq);
return "/WEB-INF/view/joinSuccess.jsp";
} catch (DuplicateIdException e) {
errors.put("duplicateId", Boolean.TRUE);
return FORM_VIEW;
}
}
}
30.폴더 생성 (폴더명 : view)
31. JoinForm 구현
경로 : WebContent > WEB-INF > view > joinForm.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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>가입</title>
</head>
<body>
<form action="join.do" method="post">
<p>
아이디 : <br />
<input type="text" name="id" value="${param.id }"/>
<c:if test="${errors.id }">ID를 입력하세요.</c:if>
<c:if test="${errors.duplicateId }">이미 사용중인 아이디입니다.</c:if>
</p>
<p>
이름: <br />
<input type="text" name="name" value="${param.name }" />
<c:if test="${errors.name }">이름을 입력하세요.</c:if>
</p>
<p>
암호: <br />
<input type="password" name="password"/>
<c:if test="${errors.password }">암호를 입력하세요.</c:if>
</p>
<p>
확인: <br />
<input type="password" name="confirmPassword"/>
<c:if test="${errors.confirmPassword }">확인을 입력하세요.</c:if>
<c:if test="${errors.notMatch }">암호와 확인이 일치하지 않습니다.</c:if>
</p>
<input type="submit" value="가입" />
</form>
</body>
</html>
32. joinSuccess 구현
경로 : WebContent > WEB-INF > view > joinSuccess.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>가입 완료</title>
</head>
<body>
${param.name}님 회원 가입이 완료되였습니다.
</body>
</html>
33.commandHandlerURI.properties 수정
핸들러 코드를 작성했으니 빈 파일로 작성되었던 commandHandlerURI.properties 를 수정해준다.
경로 : WebContent > WEB-INF > commandHandlerURI.properties
/join.do=member.command.JoinHandler
자 이제 브라우저에 join.do 주소를 입력하면 아래의 창이 나온다.
가입을 진행해보면 DB에 데이터가 잘 들어가는것을 확인할 수 있다.
http://localhost:8080/board/join.do
다음글 : 로그인 구현
https://nullgoyatten.tistory.com/19
'IT > JSP & Servlet' 카테고리의 다른 글
JSP & Servlet 게시판 구현하기(로그아웃) (0) | 2023.01.13 |
---|---|
JSP & Servlet 게시판 구현하기(로그인) (0) | 2023.01.12 |
이클립스 프로젝트 저장하기 & 불러오기(Archive) (0) | 2022.04.20 |
이클립스 주석 깨짐 ( UTF-8) 설정법 (1) | 2022.04.20 |
이클립스(Eclipse) War파일 내보내기 & 배포하기 (+docBase 설정) (0) | 2021.11.11 |