[Spring] Spring 웹 사이트 만들기 3 - 메인 페이지 만들기, 로그인 기능

2022. 11. 18. 10:02·📗 self-study/📗 KH정보교육원 당산지원

 

🔥 메인 페이지 만들기 🔥 

 

💻 main.jsp 생성

👉🏻 WAS가 관리하는 영역이므로 보안상 직접 접근 불가함

  

💻  index.jsp에 main.jsp 포워드

	 <jsp:forward page="WEB-INF/views/main.jsp" />

 

💻 header.jsp, footer.jsp 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <!-- jQuery 라이브러리 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <!-- 부트스트랩에서 제공하고 있는 스타일 -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <!-- 부트스트랩에서 제공하고 있는 스크립트 -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
    <style>
        div {box-sizing:border-box;}
        #header {
            width:80%;
            height:100px;
            padding-top:20px;
            margin:auto;
        }
        #header>div {width:100%; margin-bottom:10px;}
        #header_1 {height:40%;}
        #header_2 {height:60%;}

        #header_1>div{
            height:100%;
            float:left;
        }
        #header_1_left {width:30%; position:relative;}
        #header_1_center {width:40%;}
        #header_1_right {width:30%;}

        #header_1_left>img {height:80%; position:absolute; margin:auto; top:0px; bottom:0px; right:0px; left:0px;}
        #header_1_right {text-align:center; line-height:35px; font-size:12px; text-indent:35px;}
        #header_1_right>a {margin:5px;}
        #header_1_right>a:hover {cursor:pointer;}

        #header_2>ul {width:100%; height:100%; list-style-type:none; margin:auto; padding:0;}
        #header_2>ul>li {float:left; width:25%; height:100%; line-height:55px; text-align:center;}
        #header_2>ul>li a {text-decoration:none; color:black; font-size:18px; font-weight:900;}

        #header_2 {border-top:1px solid lightgray;}

        #header a {text-decoration:none; color:black;}

        /* 세부페이지마다 공통적으로 유지할 style */
        .content {
            background-color:rgb(247, 245, 245);
            width:80%;
            margin:auto;
        }
        .innerOuter {
            border:1px solid lightgray;
            width:80%;
            margin:auto;
            padding:5% 10%;
            background-color:white;
        }

    </style>
</head>
<body>

    <div id="header">
        <div id="header_1">
            <div id="header_1_left">
                <img src="https://www.iei.or.kr/resources/images/common/top_logo_s.jpg" alt="">
            </div>
            <div id="header_1_center"></div>
            <div id="header_1_right">
                <!-- 로그인 전 -->
                <a href="">회원가입</a>
                <a data-toggle="modal" data-target="#loginModal">로그인</a> <!-- 모달의 원리 : 이 버튼 클릭시 data-targer에 제시되어있는 해당 아이디의 div요소를 띄워줌 -->
                
                <!-- 로그인 후 -->
                <!-- 
                    <label>홍길동님 환영합니다</label> &nbsp;&nbsp;
                    <a href="">마이페이지</a>
                    <a href="">로그아웃</a>
                -->
            </div>
        </div>
        <div id="header_2">
            <ul>
                <li><a href="">HOME</a></li>
                <li><a href="">공지사항</a></li>
                <li><a href="">자유게시판</a></li>
                <li><a href="">사진게시판</a></li>
            </ul>
        </div>
    </div>

    <!-- 로그인 클릭 시 뜨는 모달 (기존에는 안보이다가 위의 a 클릭 시 보임) -->
    <div class="modal fade" id="loginModal">
        <div class="modal-dialog modal-sm">
            <div class="modal-content">
                <!-- Modal Header -->
                <div class="modal-header">
                    <h4 class="modal-title">Login</h4>
                    <button type="button" class="close" data-dismiss="modal">&times;</button>
                </div>
        
                <form action="로그인요청받아주는서버" method="post">
                    <!-- Modal body -->
                    <div class="modal-body">
                        <label for="userId" class="mr-sm-2">ID : </label>
                        <input type="text" class="form-control mb-2 mr-sm-2" placeholder="Enter ID" id="userId" name=""> <br>
                        <label for="userPwd" class="mr-sm-2">Password : </label>
                        <input type="password" class="form-control mb-2 mr-sm-2" placeholder="Enter Password" id="userPwd" name="">
                    </div>
                           
                    <!-- Modal footer -->
                    <div class="modal-footer">
                        <button type="submit" class="btn btn-primary">로그인</button>
                        <button type="button" class="btn btn-danger" data-dismiss="modal">취소</button>
                    </div>
                </form>
            </div>
        </div>

    </div>
    
    <br clear="both">
</body>
</html>

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        /* div{border:1px solid red;} */
        #footer {
            width:80%;
            height:200px;
            margin:auto;
            margin-top:50px;
        }
        #footer-1 {
            width:100%;
            height:20%;
            border-top:1px solid lightgray;
            border-bottom:1px solid lightgray;
        }
        #footer-2 {width:100%; height:80%;}
        #footer-1, #footer-2 {padding-left:50px;}
        #footer-1>a {
            text-decoration:none;
            font-weight:600;
            margin:10px;
            line-height:40px;
            color:black;
        }
        #footer-2>p {
            margin:0;
            padding:10px;
            font-size:13px;
        }
        #p2 {text-align:center;}
    </style>
</head>
<body>
    <div id="footer">
        <div id="footer-1">
            <a href="#">이용약관</a> | 
            <a href="#">개인정보취급방침</a> | 
            <a href="#">인재채용</a> | 
            <a href="#">고객센터</a>
        </div>

        <div id="footer-2">
            <p id="p1">
                강남지원 1관 : 서울특별시 강남구 테헤란로14길 6 남도빌딩 2F, 3F, 4F, 5F, 6F <br>
                강남지원 2관 : 서울특별시 강남구 테헤란로10길 9 그랑프리 빌딩 4F, 5F, 7F <br>
                강남지원 3관 : 서울특별시 강남구 테헤란로 130 호산빌딩 5F, 6F <br>
                종로지원 : 서울특별시 중구 남대문로 120 대일빌딩 2F, 3F <br>
                당산지원 : 서울특별시 영등포구 선유동2로 57 이레빌딩 (구관) 19F, 20F
            </p>
            <p id="p2">Copyright © 1998-2022 KH Information Educational Institute All Right Reserved</p>
        </div>
    </div>
</body>
</html>

 

💻  main.jsp에 header.jsp, footer.jsp 페이지를 include

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<jsp:include page="common/header.jsp" />
	
	<div style="height:600px;">
	
	</div>
	
	<jsp:include page="common/footer.jsp" />

</body>
</html>

 

 

 

🔥 로그인 기능 🔥

 

💻 Member 클래스 구성

package com.kh.spring.member.model.vo;

import java.sql.Date;

public class Member {
	
	private String userId; 	 //	USER_ID	VARCHAR2(30 BYTE)
	private String userPwd;  //	USER_PWD	VARCHAR2(100 BYTE)
	private String userName; //	USER_NAME	VARCHAR2(15 BYTE)
	private String email; 	 //	EMAIL	VARCHAR2(100 BYTE)
	private String gender; 	 //	GENDER	VARCHAR2(1 BYTE)
	private int age; 		 //	AGE	NUMBER
	private String phone; 	 //	PHONE	VARCHAR2(13 BYTE)
	private String address;  //	ADDRESS	VARCHAR2(100 BYTE)
	private Date enrollDate; //	ENROLL_DATE	DATE
	private Date modifyDate; //	MODIFY_DATE	DATE
	private String status;   //	STATUS	VARCHAR2(1 BYTE)
	
	public Member() { }

	public Member(String userId, String userPwd, String userName, String email, String gender, int age, String phone,
			String address, Date enrollDate, Date modifyDate, String status) {
		super();
		this.userId = userId;
		this.userPwd = userPwd;
		this.userName = userName;
		this.email = email;
		this.gender = gender;
		this.age = age;
		this.phone = phone;
		this.address = address;
		this.enrollDate = enrollDate;
		this.modifyDate = modifyDate;
		this.status = status;
	}

	public String getUserId() {
		return userId;
	}

	public void setUserId(String userId) {
		this.userId = userId;
	}

	public String getUserPwd() {
		return userPwd;
	}

	public void setUserPwd(String userPwd) {
		this.userPwd = userPwd;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Date getEnrollDate() {
		return enrollDate;
	}

	public void setEnrollDate(Date enrollDate) {
		this.enrollDate = enrollDate;
	}

	public Date getModifyDate() {
		return modifyDate;
	}

	public void setModifyDate(Date modifyDate) {
		this.modifyDate = modifyDate;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	@Override
	public String toString() {
		return "Member [userId=" + userId + ", userPwd=" + userPwd + ", userName=" + userName + ", email=" + email
				+ ", gender=" + gender + ", age=" + age + ", phone=" + phone + ", address=" + address + ", enrollDate="
				+ enrollDate + ", modifyDate=" + modifyDate + ", status=" + status + "]";
	}

}

 

💻 header.jsp

👉🏻 taglib 지시어 추가

👉🏻 로그인 전후 조건 걸기

👉🏻 로그인 폼에 action 속성 걸고 name 속성 추가

// 상단에 taglib 지시어 추가
<%@ 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>


// body 영역에 로그인 전후 조건 추가 및 폼 action, name 속성 수정
<body>

    <div id="header">
        <div id="header_1">
            <div id="header_1_left">
                <img src="https://www.iei.or.kr/resources/images/common/top_logo_s.jpg" alt="">
            </div>
            <div id="header_1_center"></div>
            <div id="header_1_right">          
                <c:choose>
                	<c:when test="${ empty loginUser }">
		                <!-- 로그인 전 -->
		                <a href="">회원가입</a>
		                <a data-toggle="modal" data-target="#loginModal">로그인</a>
		                <!-- 모달의 원리 : 이 버튼 클릭시 data-targer에 제시되어있는 해당 아이디의 div요소를 띄워줌 -->
                	</c:when>
                	<c:otherwise>            	                
		                <!-- 로그인 후 -->
	                    <label>${ loginUser.userName }님 환영합니다</label> &nbsp;&nbsp;
	                    <a href="">마이페이지</a>
	                    <a href="">로그아웃</a>
                	</c:otherwise>
                </c:choose>
            </div>
        </div>
        <div id="header_2">
            <ul>
                <li><a href="">HOME</a></li>
                <li><a href="">공지사항</a></li>
                <li><a href="">자유게시판</a></li>
                <li><a href="">사진게시판</a></li>
            </ul>
        </div>
    </div>

    <!-- 로그인 클릭 시 뜨는 모달 (기존에는 안보이다가 위의 a 클릭 시 보임) -->
    <div class="modal fade" id="loginModal">
        <div class="modal-dialog modal-sm">
            <div class="modal-content">
                <!-- Modal Header -->
                <div class="modal-header">
                    <h4 class="modal-title">Login</h4>
                    <button type="button" class="close" data-dismiss="modal">&times;</button>
                </div>
        
                <form action="login.me" method="post">
                    <!-- Modal body -->
                    <div class="modal-body">
                        <label for="userId" class="mr-sm-2">ID : </label>
                        <input type="text" class="form-control mb-2 mr-sm-2" placeholder="Enter ID" id="userId" name="userId"> <br>
                        <label for="userPwd" class="mr-sm-2">Password : </label>
                        <input type="password" class="form-control mb-2 mr-sm-2" placeholder="Enter Password" id="userPwd" name="userPwd">
                    </div>
                           
                    <!-- Modal footer -->
                    <div class="modal-footer">
                        <button type="submit" class="btn btn-primary">로그인</button>
                        <button type="button" class="btn btn-danger" data-dismiss="modal">취소</button>
                    </div>
                </form>
            </div>
        </div>

    </div>
    
    <br clear="both">
</body>

 

💻 회원과 관련된 모든 요청을 처리하는 MemberController 클래스 생성

💡 Controller의 코드가 달라져서 놀라셨나요? 아래 링크를  참고해 볼 것!

📖 Spring에서 Controller는 어떻게 달라졌을까?

  

👉🏻 서블릿의 개념이 없는 건 아니지만 DispatcherServlet이 모두 처리해 주기 때문에 서블릿이 아닌 클래스로 생성함!

👉🏻 내가 만든 클래스이므로 어노테이션을 기재함으로써 Spring에 bean 등록할 것!

 

	@RequestMapping("login.me")
	public ModelAndView loginMember(Member m, ModelAndView mv, HttpSession session) {
		
		Member loginUser = memberService.loginMember(m);
		
		if(loginUser == null) { // 로그인 실패
			
			// model.addAttribute("errorMsg", "로그인 실패");
			mv.addObject("errorPage", "로그인 실패");
			
			// return "common/errorPage";
			mv.setViewName("common/errorPage"); // 포워딩
			// ModelAndView 객체를 이용해서 응답페이지를 지정할 경우 또한 ViewResolver에 의해 접두어와 접미어를 생략한 형태로 지정해야만 함
			
		} else { // 로그인 성공
			
			session.setAttribute("loginUser", loginUser);
			
			// return "redirect:/";
			mv.setViewName("redirect:/"); // url 재요청방식
			
		}
		
		// 어느 경우에나 mv를 리턴하기 때문에 따로 2번 적을 필요 없이 조건문 밖에 기술해도 됨!
		return mv;
		
	}

 

💻 errorPage.jsp 생성

📖 404 오류 주의! 경로 중복 출력 시 참고하기 (/WEB-INF/views와 .jsp 중복 출력될 때)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <jsp:include page="header.jsp" />

    <br>
    <div align="center">
        <img src="https://cdn2.iconfinder.com/data/icons/oops-404-error/64/208_balloon-bubble-chat-conversation-sorry-speech-256.png">
        <br><br>
        <h1 style="font-weight:bold;">${ errorMsg }</h1>
    </div>
    <br>

    <jsp:include page="footer.jsp" />
    
</body>
</html>

 

💻 회원과 관련된 모든 요청을 처리하는 MemberService 인터페이스에 메소드 정의

package com.kh.spring.member.model.service;

import com.kh.spring.member.model.vo.Member;

public interface MemberService {
	
	// 로그인 서비스(select)
	Member loginMember(Member m);
	
	// 회원가입 서비스 (insert)
	int insertMember(Member m);
	
	// 회원 정보 수정 서비스 (update)
	int updateMember(Member m);
	
	// 회원 탈퇴 서비스 (update)
	int deleteMember(String userId);
	
	// 아이디 중복체크 서비스 (select)
	int idCheck(String checkId);

}

 

💻 MemberServiceimpl 클래스에 메소드 Add 추가 후 로그인 처리

✔️ 클래스에 @Service 어노테이션 꼭 붙여 주기!

 

순수 MyBatis에서는 SqlSession 객체가 필요했으나 스프링에서 MyBatis를 쓰려면 SqlSessionTemplate 객체가 필요함

👉🏻root-context.xml에서 bean으로 등록했었음
✔️
 내가 직접 new 구문을 통해 객체를 생성할 필요가 없음(@전역변수에 @Autowired 어노테이션으로 선언)

 

객체 생성을 스프링이 했다는 것

그것은 내가 객체를반납(close)해 줄필요도없다는 것....✨

package com.kh.spring.member.model.service;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.kh.spring.member.model.dao.MemberDao;
import com.kh.spring.member.model.vo.Member;

// @Conponent // 도 가능하긴 함!
@Service
public class MemberServiceImpl implements MemberService {

	// Spring에서 MyBatis를 사용하기 위한 SqlSessionTemplate 변수 선언
	@Autowired
	private SqlSessionTemplate sqlSession;
	
	// MemberDao 객체 생성 선언문
	@Autowired
	private MemberDao memberDao;
	
	@Override
	public Member loginMember(Member m) {
		
		/*
		 * 기존의 순수 MyBatis 코드
		 * SqlSession sqlSession = Template.getSqlSession();
		 * // 이 시점에서 객체 생성됨
		 * 
		 * Member loginUser = new MemberDao().loginMember(sqlSession, m);
		 * // 이 시점에서 객체 생성됨
		 * 
		 * sqlSession.close();
		 * return loginUser;
		 */
		
		Member loginUser = memberDao.loginMember(sqlSession, m);
        
        	// SqlSessionTemplate 객체를 bean으로 등록 후 @Autowired 해 줌으로써
		// Spring이 해당 객체를 생성해 사용 후 자동으로 객체를 반납함
		// 내가 직접 close 메소드로 자원 반납할 필요가 없어짐!
		
		return loginUser;
	}

	@Override
	public int insertMember(Member m) {
		return 0;
	}

	@Override
	public int updateMember(Member m) {
		return 0;
	}

	@Override
	public int deleteMember(String userId) {
		return 0;
	}

	@Override
	public int idCheck(String checkId) {
		return 0;
	}

}

 

💻 MemberDao

👉🏻 @Repository:Dao 역할을 하는 bean으로 등록하겠다는 뜻

       Repository: 저장소라는 뜻으로 주로 DB(저장소)와 관련된 작업인 영속성 작업을 처리하겠다라는 것!

package com.kh.spring.member.model.dao;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import com.kh.spring.member.model.vo.Member;

@Repository // Dao 역할을 하는 bean으로 등록하겠다!
// 주로 DB(저장소)와 관련된 작업 (== 영속성 작업)을 처리하겠다라는 뜻
public class MemberDao {
	
	public Member loginMember(SqlSessionTemplate sqlSession, Member m) {
		
		/*
		 * SQL문 종류에 따른 메소드를 호출
		 * => SqlSessio 객체와 SqlSessionTemplate 객체에서 제공하는 메소드 종류가 동일
		 */
		
		// 로그인 == 단일행 조회
		// sqlSession.selectOne("mapper파일의namespace.sql구문의id값",(쿼리문이미완성이라면)넘길값);
		return sqlSession.selectOne("memberMapper.loginMember", m);
		
	}

}

 

💻 member-mapper.xml

✔ parameterType="com.kh.spring.member.model.vo.Member" 👉🏻 parameterType="member"

✔ 내가 넘겨 받을 값은 "해당 경로" 안에 있는 값이야~ 👉🏻 근데 내가 mybatis-config.xml에 typeAliase 설정해 놓은 거 알지?? 그 별칭으로 쓸게!

 

☑️ 테이블로부터 조회된 내용을 원하는 객체 타입으로 가공해 주는 ResultMap

☑️  <resultMap id="memberResultSet" type="member">

☑️  memberResultSet이라는 이름으로 member라는 객체를 가공할 거야~

☑️ <result column="DB의 어느 컬럼 값을" property="Java의 어느 필드에 넣을 건지" />

☑️ property 오타 시 setter를 찾을 수 없다는 오류, column 오타 시 부적합한 식별자 오류 뜸!

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

	<!-- MEMBER 테이블로부터 조회된 내용을 Member 타입으로 가공해 주는 ResultMap -->
	<resultMap id="memberResultSet" type="member">
		<result column="USER_ID" property="userId" />
		<result column="USER_PWD" property="userPwd" />
		<result column="USER_NAME" property="userName" />
		<result column="EMAIL" property="email" />
		<result column="GENDER" property="gender" />
		<result column="AGE" property="age" />
		<result column="PHONE" property="phone" />
		<result column="ADDRESS" property="address" />
		<result column="ENROLL_DATE" property="enrollDate" />
		<result column="MODIFY_DATE" property="modifyDate" />
		<result column="STATUS" property="status" />
	</resultMap>
	
	<!-- 로그인용 쿼리문 -->
	<select id="loginMember" parameterType="member" resultMap="memberResultSet">
		SELECT *
		FROM MEMBER
		WHERE USER_ID = #{userId}
		AND USER_PWD = #{userPwd}
		AND STATUS = 'Y'	
	</select>
</mapper>

 

 

 

🔥 로그아웃 기능 🔥

 

💻 header.jsp 로그아웃 url 설정

                	<c:otherwise>            	                
		                <!-- 로그인 후 -->
	                    <label>${ loginUser.userName }님 환영합니다</label> &nbsp;&nbsp;
	                    <a href="">마이페이지</a>
	                    <a href="logout.me">로그아웃</a>
                	</c:otherwise>

 

💻 MemberController

	@RequestMapping("logout.me")
	public String logoutMember(HttpSession session) {
		
		// 세션 무효화 메소드: session.invalidate();
		session.invalidate();
		
		// 메인 페이지로 url 요청
		// mv.setViewName("redirect:/"); // 반환형 수정, ModelAndView 매개변수 요청 후 이렇게 해도 무방!
		return "redirect:/";
	}
저작자표시 비영리 변경금지 (새창열림)
'📗 self-study/📗 KH정보교육원 당산지원' 카테고리의 다른 글
  • [Spring] Lombok (롬복) - 코드를 자동으로 생성해 주는 라이브러리
  • [Spring] Spring 웹 사이트 만들기 4 - 회원가입 기능
  • [Spring] 파라미터(요청 시 전달값)를 받는 방법 / 요청 처리 후 응답 페이지를 담고 포워딩 또는 url 재요청 하는 방법
  • [Spring] Spring에서 Controller는 어떻게 달라졌을까?
천재강쥐
천재강쥐
  • 천재강쥐
    디버거도 버거다
    천재강쥐
  • 전체
    오늘
    어제
    • Category (467)
      • 진짜 너무 궁금한데 이걸 나만 몰라...? (0)
      • 💾 Portfolio (2)
      • 🐤 CodingTest (28)
        • Java (20)
        • ᕕ(ꐦ°᷄д°᷅)ᕗ❌ (5)
      • 🚀 from error to study (142)
        • AI (1)
        • Cloud (2)
        • DB (12)
        • Front-End (16)
        • Github (14)
        • Java (39)
        • Mac (7)
        • Normal (29)
        • Server (22)
      • 📘 certificate (44)
        • 📘 리눅스마스터1급 (1)
        • 📘⭕️ 정보처리기사 (40)
        • 📘⭕️ SQLD (3)
      • 📗 self-study (234)
        • 📗 inflearn (35)
        • 📗 생활코딩 (8)
        • 📗 KH정보교육원 당산지원 (190)
      • 🎨 Scoop the others (0)
        • 📖 Peeking into other people.. (0)
        • 🇫🇷 (0)
        • 📘⭕️ 한국사능력검정시험 심화 (11)
        • 오블완 (4)
  • 인기 글

  • hELLO· Designed By정상우.v4.10.1
천재강쥐
[Spring] Spring 웹 사이트 만들기 3 - 메인 페이지 만들기, 로그인 기능
상단으로

티스토리툴바