-
전자정부프레임워크
- 표준프레임워크 개발자 교육 교재 및 실습 for Win (V4.0.0)
- https://www.egovframe.go.kr/home/ntt/nttRead.do?pagerOffset=0&searchKey=&searchValue=&menuNo=65&bbsId=4&nttId=1743
- 다운로드 받고, 반드시 C:\ 경로에 저장할 것
-
스프링, 자바 업데이트 -pom.xml
- 스프링 4.0.0 => 5.0.2로 최신화
- 자바 1.6 => 1.8로 최신화
- 프로젝트 클릭 => Alt+F5 => Force update if snapshots
-
아파치 톰캣 등록 :
- 프로젝트 끌어서 Server 탭의 Tomcat에 드래그해서 끌어 당겨야함
-
오류메시지 지우기 :
- src/main/webapp/WEB-INF/web.xml
- <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" =>
- <web-app version="2.5" xmlns="http://Java.sun.com/xml/ns/javaee"
-
한글 깨짐 해결 :
- 한글깨짐방지.txt => web.xml로 붙여넣기
라이브러리/설정 | Maven Repository 링크 | 설명 |
---|---|---|
MyBatis | MyBatis 3.4.6 | SQL 매핑 프레임워크 |
HikariCP | HikariCP 3.4.1 | 고성능 JDBC 연결 풀 |
MySQL Connector Java | MySQL 5.1.42 | MySQL 데이터베이스 드라이버 |
Spring JDBC | Spring JDBC 5.0.2.RELEASE | 스프링의 JDBC 통합 지원 |
MyBatis Spring | MyBatis-Spring 1.3.2 | MyBatis와 Spring 통합 |
루트 컨텍스트 설정.txt | 루트컨텍스트설정.txt | root-context.xml 에 붙여넣을 설정 |
-
부트스트랩 : 디자인 탬플릿 적용
-
Lombok
- https://projectlombok.org/
- https://mvnrepository.com/artifact/org.projectlombok/lombok
- 자동으로 생성자, getter/setter 생성. dto 수정 시 빠르게 대응 할 수 있음.
/* lombok => 생성자 만들어주는 api
@Data => getter/setter
@AllArgsConstructor => 전체 생성자
@NoArgsConstructor => 빈 생성자
@ToString => toString */
- C++ 다운로드
- mysql 실행 안되는 경우 :
- C:\eGovFrame-4.0.0\bin\mysql-5.7.32\startup.bat 눌렀는데, Version: '5.7.32-log' socket: '' port: 3306 MySQL Community Server (GPL)라고 뜨면 잘 되는 것임.
- https://www.microsoft.com/ko-KR/download/details.aspx?id=40784
- kr.spring.mapper
- Type : MySql_5.1 ver
- ContextPath 변경하는 법
- server.xml, context 태그, path 변경
<Context docBase="SpringMVC03" path="/controller" reloadable="true" source="org.eclipse.jst.jee.server:SpringMVC03"/></Host>
- @Controller
- @RequestMapping(value = "/", method = RequestMethod.GET)
- => HomeController.java에서 root 주소에서의 작업을 처리함
-
web.xml => 스프링 정보 총괄, 톰캣이 읽을 때 가장 먼저 읽는 파일
- Root Spring => root-context.xml (여기에 jdbc 설정 해줘야함 - 루트컨텍스트 설정.txt)
- ContextLoaderListener
- DispatcherServlet => servlet-context.xml (디스패쳐 서블릿이 생성될 때 이 파일 참조함)
-
servlet-context.xml
- <context:component-scan base-package="kr.spring.controller" />
- 여기 기본 context 매핑되어있음.
-
root-context.xml
- 루트컨텍스트 설정.txt => root-context.xml 붙여 넣기
- hikariConfig : db url, id, pw 정보 담고 있음
- dataSource : getConnection() [hikariConfig를 참조]
- SqlSessionFactoryBean : psmt, getClose() [dataSource를 참조]
- mybatis-spring:scan : kr.spring.mapper 패키지에 있는 인터페이스 찾을거임.
-
BoardMapper.java (인터페이스)
@Mapper // MyBatis 인터페이스를 찾기위해 달아주는 부분
public interface BoardMapper {
@Select("SELECT * FROM BOARD")
public List<Board> getLists();
}
-
리다이렉트 방식
- return "redirect:주소";
- return "redirect:boardList.do";
-
간편한 파라미터 받기 :
-
- 기본생성자
-
- getter/setter
-
- dto의 속성 이름 (name="DTO의 속성 이름")
- 이 셋 조건을 충족하면 request.getParameter 할 필요 없이 dto자체로 받을 수 있다.
-
-
MyBatis XML Mapper
-
- Mapper 이름 동일
-
- <select id="Mapper 매서드 이름과 동일">
-
<select id="getLists" resultType="kr.spring.entity.Board">
SELECT * FROM BOARD ORDER BY INDATE DESC
</select>
<insert id="boardInsert">
INSERT INTO BOARD (TITLE, CONTENT, WRITER)
VALUES(#{title}, #{content}, #{writer})
</insert>
-
@RequestParam("idx") int idx :
- request.getParameter과 동일한 역할.
-
줄바꿈 :
- <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
- <body> <% pageContext.setAttribute("newLineChar", "\n"); %> </body>
- ${fn:replace( vo.content, newLineChar, "<br>") }
- ${fn:split( vo.indate, " ")[0] }
-
@RequestParam("idx") int idx => int idx
- 로 짧게 써도 파라미터 가져올 수 있다.
- ※단, 파라미터의 name과 같은 변수명을 써야
-
<input type="hidden" name="idx" value="${vo.idx }">
- type="hidden" 옵션으로 데이터 더 보낼 수 있음.
-
PathVariable
- get 방식에서, key값을 설정을 안하고 값을 넘겨줄 수 있다.
-
- boardContent.do?idx=${dto.idx } => boardContent.do/${dto.idx }
-
- /boardContent.do => /boardContent.do/{idx}
-
- @RequestParam("idx") => @RequestParam("idx")
-
- ../ : 경로 안으로 들어온 처리가 되어서, 경로 밖으로 나가줘야함.
-
jakson
- json 응답 라이브러리
<!-- json 방식으로 응답하기 위한 API -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
- @ResponseBody
- 비동기임을 알려주는 어노테이션
public @ResponseBody List<Board> boardList() {}
- Ajax
<script type="text/javascript">
// 처음 실행될 때 자동 실행 => 생성자 느낌
// html 다 로딩되고 아래 코드 실행
$(document).ready(function() {
loadList();
});
function loadList() {
// 게시글 리스트 가져오기
// ajax - 요청 url, 어떻게 데이터 받을지, 요청방식 등 .. => 객체{}로 넣기
$.ajax({
url : "boardList.do",
type : "get",
dataType : "json",
success : makeView, // callback 함수
error : function() {alert("error");}
});
}
function makeView(data) {
console.log(data);
}
</script>
- jquery 반복문 활용
$.each(data, function(index, obj) {
listHtml += "<tr>";
listHtml += "<td>" + index + "</td>";
listHtml += "<td>" + obj.title + "</td>";
listHtml += "<td>" + obj.writer + "</td>";
listHtml += "<td>" + obj.indate + "</td>";
listHtml += "<td>" + obj.count + "</td>";
listHtml += "</tr>";
});
$("#view").html(listHtml);
-
- 줄바꿈
-
javascript $().serialize()
fData = $("#frm").serialize();
-
- {"키" : 값, "키" : 값}
-
- serialData = "title=" + title + "&content=" + content;
-
-
Rest 전송 방식
URL에 통일성, 요청하는 URL+전송방식 묶어서 요청 가능!
- URL의 통일성, 단순화
-
@RestController
@RestController
public class BoardRestController {
@Autowired
private BoardMapper mapper;
}
-
BoardRestController => 비동기 방식
- @ResponseBody 필요 없다.
=> 어차피 비동기 방식이기 때문!
- @ResponseBody 필요 없다.
-
매핑
- @RequestMapping("/주소")
@RequestMapping("/board")
@RestController
public class BoardRestController {
@RequestMapping("/all")
public List<Board> boardList() {...}
}
- 매핑 주소 사용 방법
- $.ajax({url : "board/주소"})
$.ajax({url : "board/all"})
- Mapping Type:
- @RequestMapping : get, post
- @GetMapping : get
- @PostMapping : post
- @DeleteMapping : delete
- @PutMapping : update
- update / put 매서드
$.ajax({
url : "board/update",
type : "put",
contentType : "application/json;charset=utf-8",
data : JSON.stringify({
"idx" : idx,
"title" : title,
"content" : content,
"writer" : writer
}),
...
})
@PutMapping
@RequestMapping("/update")
public void boardUpdate(@RequestBody Board board) {
System.out.println("게시글 수정 수행");
mapper.boardUpdate(board);
}
@RequestBody :
자바 객체로 변환해주는 것임.
data를 JSON.stirngify()로 json => stirng으로 변환하므로 자바 객체로 변환해줄 필요가 있다.
-
네비게이션 바 BOOTSTRAP
-
contextPath 전역변수로 지정
<c:set var="contextPath" value="${pageContext.request.contextPath}"></c:set>
- modal :
- modal bootstrap
- 사용자의 이목을 집중시키기 위한 그래픽 인터페이스 창
$("#checkMessage").text("사용할 수 있는 아이디입니다.");
$("#myModal").modal("show");
- RedirectAttributes
- 리다이렉트 방식으로 이동할 때 보낼 데이터를 저장하는 객체
- 리다이렉트할 때 보내고 사라짐
- (↔ forward model)
// 매개변수 받기
public String join(RedirectAttributes rttr);
// 사용하기
rttr.addFlashAttribute("msgType", "실패메세지");
rttr.addFlashAttribute("msg", "모든 내용을 채워주세요.");
// 모델 사용 복습 (포워드 방식)
public String boardlist(Model model)
model.addAttribute("list", list);
- $(document).ready()
- 모든 문서가 로드 되었을 때 쓰는 자바스크립트 함수
<script>
$(document).ready(function() {})
</script>
- required
- input란이 공백이면 제출할 수 없음
<input required>
- HttpSession
- 스프링의 세션사용
public String join(HttpSession session)
session.setAttribute("member", member);
- 프로젝트 임포트하는법
- 이름이 같은 프로젝트 Delete, 실제 경로로 들어가서 그 프로젝트 있는지 확인
- 같은 경로로 .zip 파일 붙여넣기
- import-import-next-archaive-.zip파일 선택
- build path-JRE System Library - Workspace default JRE
- Server Runtime 연동 안되어있으면, Add Library-Server Runtime-Finish
- 서버 등록
- webapp/resources
- 이미지 등 저장공간
- servlet-context.xml에 매핑되어있음.
<resources mapping="/resources/**" location="/resources/" />
- bootstrap tabs
https://www.w3schools.com/bootstrap/tryit.asp?filename=trybs_tabs_dynamic&stacked=h
- c:if - 라디오 버튼
<c:if test="${member.memGender eq '남자'}"></c:if>
<c:if test="${member.memGender eq '여자'}"></c:if>
- insert, update : cnt
- update, insert 성공적으로 수행했으면 cnt=1
int cnt = mapper.update(member);
<span class="glyphicon glyphicon-log-in">로그인</span>
<span class="glyphicon glyphicon-log-out">로그아웃</span>
- 파일업로드 jsp
<form action="${contextPath }/imageUpdate.do" method="post" enctype="multipart/form-data">
<input type="file" name="memProfile">
</form>
- 파일업로드 maven
<!-- https://mvnrepository.com/artifact/servlets.com/cos -->
<dependency>
<groupId>servlets.com</groupId>
<artifactId>cos</artifactId>
<version>05Nov2002</version>
</dependency>
- 파일업로드 controller
- 파일업로드 객체는 파라미터로 받아올 수 없다.
- 받아올 수 있는 5가지
데이터, 저장경로, 최대 크기, 인코딩, 파일명 중복제거
public String imageUpdate(HttpServletRequest request) {
MultipartRequest multi = null;
String savePath = request.getRealPath("resources/upload");
int fileMaxSize = 10 * 1024 * 1024;
try {
multi = new MultipartRequest(request, savePath, fileMaxSize, "utf-8", new DefaultFileRenamePolicy());
} catch (IOException e) {
e.printStackTrace();
}
...
}
-
resource/upload 폴더 생성
-
보내면 다음 경로에 파일이 업로드 되어있는 것을 볼 수 있다 (서버 측 폴더)
C:\eGovFrame-4.0.0\workspace.edu.metadata.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\SpringMVC03\resources\upload
- 이전 프로 파일 삭제
String oldImg = mapper.getMember(memId).getMemProfile();
File oldFile = new File(savePath + "/" + oldImg);
if(oldFile.exists()) {
oldFile.delete();
}
- 파일 무결성 검사
// 내가 방금 업로드한 파일 정보
File file = multi.getFile("memProfile");
if (file != null) {
// 확장자 가져오기
String ext = file.getName().substring(file.getName().lastIndexOf(".") + 1);
// PNG, png
ext = ext.toUpperCase();
if (!(ext.equals("PNG") || ext.equals("GIF") || ext.equals("JPG") || ext.equals("JPEG"))) {
if(file.exists()) {
file.delete();
rttr.addFlashAttribute("msgType", "실패메세지");
rttr.addFlashAttribute("msg", "이미지 파일만 가능합니다.(png, jpg, gif)");
return "redirect:/imageForm.do";
}
}
}
SpringMVC03 끝 SpringMVC04
- 조건문 안에서 el 식을 쓰고 싶다면 문자열로 감싸주어야 한다
<script>
if("${mvo.memId}" == obj.memId) {...}
console.log("member.memId : "+"${member.memId}");
</script>
- web.xml 안쓰겠습니다 선언
pom.xml
<build>
<plugins>
<!-- web.xml 안쓰겠습니다 선언. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
...
- .xml 삭제
- Config.java 생성
WebConfig.java
ServletConfig.java
RootConfig.java
- AbstractAnnotationConfigDispatcherServletInitializer (추상 클래스) 상속
- add unimplement method (추상 클래스를 상속받았으므로 필수 함술르 만들어줘야함)
설정 유형 | XML 설정 파일 | Java 설정 파일 | 설명 |
---|---|---|---|
웹 설정 | web.xml | WebConfig.java | 웹 컨텍스트 관련 설정. 서블릿 매핑과 컨텍스트 파라미터를 포함합니다. |
루트 설정 | root-context.xml | RootConfig.java | 애플리케이션 컨텍스트 설정. 데이터 소스, 서비스, 레포지토리 등을 포함합니다. |
서블릿 설정 | servlet-context.xml | ServletConfig.java | 서블릿 컨텍스트 관련 설정. 뷰 리졸버와 컨트롤러를 포함합니다. @ComponentScan 을 사용하여 여러 컨트롤러를 지정할 수 있습니다. |
src/main/resources/persistence-mysql.properties
- db 연결을 위한 설정파일
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/com
jdbc.user=com
jdbc.password=com01
RootConfig.java (연결)
@PropertySource({"classpath:persistence-mysql.properties"})
public class RootConfig {
@Autowired
Environment env;
...
}
SpringMVC04
SpringMVO05
- security 버전 추가 & org.springframework.security 추가.
pom.xml
<!-- Spring Security를 하기위한 버전 추가-->
<org.springsecurity-version>5.0.2.RELEASE</org.springsecurity-version>
<!-- Spring Security API 추가 -->
<dependency>
<!-- web에서 Srpnig Security를 사용하기 위한 api -->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springsecurity-version}</version>
</dependency>
<dependency>
<!-- Spring Security를 사용하기 위한 API -->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springsecurity-version}</version>
</dependency>
<dependency>
<!-- Spring Security를 편리하게 사용하기위한 태그라이브러리 사용 API -->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${org.springsecurity-version}</version>
</dependency>
- 위 두 문장을 추가하면 스프링 버전이 업데이트 되므로 alt+f5 - Force update 체크해서 업데이트 해줘야함.
- AbstractSecurityWebApplicationInitializer
kr.spring.config/SecurityInitializer.java
- 자동으로 보안 관련 객체들이 생성되어 스프링 컨테이너(메모리 공간)으로 올라간다
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer{}
- WebSecurityConfigurerAdapter
kr.spring.config/SecurityConfig
- CSRF
Cross-Site Request Forgery. 사이트 간 요청 위조의 줄임말.
- CSRF 설정을 하지 않으면 로그인, 회원가입 등 403 에러가 뜬다.
- 해결 방안 : 토큰을 줘서 확인함
joinForm.jsp
loginForm.jsp
updateForm.jsp
<!-- 보안 토큰 설정 -->
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
imageForm.jsp
<!-- 보안 토큰 설정 (get 방식으로 내보내기) -->
<form action="${contextPath }/imageUpdate.do?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">
main.jsp (javascript 비동기 통신)
<script>
var csrfHeaderName = "${_csrf.headerName}";
var csrfTokenValue = "${_csrf.token}";
$.ajax({
...
beforeSend : function(xhr){
xhr.setRequestHeader(csrfHeaderName, csrfTokenValue);
},
...
});
</script>
웹페이지
<!-- 다음과 같이 자동으로 csrf 토큰 값이 들어오는 걸 볼 수 있다. -->
<input type="hidden" name="_csrf" value="04872be9-ce98-4b5b-a42c-2070b798c971">
4-1. CSRF UTF-8 인코딩
SecurityConfig.java
- 보안쪽 클래스를 지나면서 인코딩이 깨진다. 따라서 인코딩을 Security쪽에 따로 해줘야
@Override
protected void configure(HttpSecurity http) throws Exception {
// 요청에 대한 보안 설정하는 곳
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("utf-8");
filter.setForceEncoding(true);
}
- Auth 테이블 생성
create table auth(
no int not null auto_increment,
memid varchar(50) not null,
auth varchar(50) not null,
primary key(no),
constraint fk_member_auth foreign key(memid) references member(memid)
);
-
form 태그에 권한 엔티티 추가 및 무결성검사
-
bean으로 패스워드 인코더 등록
SecurityConfig
@Bean // password 인코딩 기능을 메모리에 올리기
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//사용
@Autowired
private PasswordEncoder pwEncoder;
// 비밀번호 암호화하기
String encyPw = pwEncoder.encode(member.getMemPassword());
System.out.println(encyPw);
확인해보면 '$2a$10$eE3kIhtbnLAELjZECaucgenZSMJFHbO1AZuIuOL3enjg4keEqyJke'와 같이 암호화 된 것을 볼 수 있다.
- DB 테이블이 변경되었으므로 ID="JOIN" SQL문 수정
<insert id="join" parameterType="kr.spring.entity.Member">
INSERT INTO MEMBER(MEMIDX, MEMID, MEMPASSWORD, MEMNAME, MEMAGE, MEMGENDER,
MEMEMAIL, MEMPROFILE)
VALUES(
(SELECT IFNULL(MAX(MEMIDX) + 1, 1) FROM MEMBER MEM),
#{memID}, #{memPassword}, #{memName}, #{memAge},
#{memGender}, #{memEmail}, #{memProfile}
)
</insert>
a. <resultMap>
1. result를 추가해주기 위한 도구
2. 배열 받아오기 위해선 <collection />
<resultMap type="kr.spring.entity.Auth" id="authMap">
<id property="no" column="no"/>
<result property="memId" column="memId"/>
<result property="auth" column="auth"/>
</resultMap>
<resultMap type="kr.spring.entity.Member" id="memberMap">
<id property="memIdx" column="memIdx"/>
<result property="memId" column="memId"/>
<result property="memPassword" column="memPassword"/>
<result property="memName" column="memName"/>
<result property="memAge" column="memAge"/>
<result property="memGender" column="memGender"/>
<result property="memEmail" column="memEmail"/>
<result property="memProfile" column="memProfile"/>
<collection property="authList" resultMap="authMap"/>
</resultMap>
<select id="getMember" parameterType="String" resultMap="memberMap">
SELECT *
FROM MEMBER mem LEFT OUTER JOIN AUTH auth
ON mem.MEMID = auth.MEMID
WHERE mem.MEMID = #{memId}
</select>
b. 비밀번호 일치여부 체크
pwEncoder.matches()
- true, false 반환
boolean pwCheck = pwEncoder.matches(member.getMemPassword(), m.getMemPassword());
SpringMVC06
- 계정 정보 보관 장소 : Session → Spring Context Holder
a. 권한 설정
SecurityConfig.java
- 로그인
- 로그아웃
- 특정페이지 권한 설정
http
.authorizeRequests() /* 요청에 따른 권한을 처리하겠다 */
.antMatchers("/") /* 어떠한 경로로 왔을 때 권한처리를 할 것인지 */
.permitAll() /* 누구나 접근 가능하게끔 전체 권한 */
.and()
.formLogin() /* 로그인 보안기능 추가 */
.loginPage("/loginForm.do")
.loginProcessingUrl("/login.do")
.permitAll()
.and()
.logout() /* 로그아웃기능 */
.invalidateHttpSession(true)
.logoutSuccessUrl("/")
.and()
.exceptionHandling().accessDeniedPage("/access-denied"); /* 로그인도 안하고 특정페이지에 접근하려고할 때 막기 */
MemberController.java
access-denied.jsp
@GetMapping("/access-denied")
public String showAccessDenied() {
return "access-denied";
}
b. Spring Security와 mapper 연결
- UserDetailsService 인터페이스 상속
- 메서드를 가져와야 한다.
- mapper의 로그인 기능을 연결.
c. 스프링 보안 엔티티
- org.springframework.security.core.userdetails.User 상속
- Member 엔티티 객체로 가져옴
d. 로그인 매퍼
MemberMapper.xml
<select id="login" resultMap="memberMap">
SELECT * FROM MEMBER mem LEFT OUTER JOIN Auth auth on
mem.MEMID = auth.MEMID WHERE mem.MEMID = #{username}
</select>
e. MemberUserDetailsService 사용
kr.spring.config/SecurityConfig.java
@Bean // 우리가 만든 MemberUserDetailsService 메모리로 올려 사용하겠다.
public UserDetailsService MemberUserDetailsService() {
return new MemberUserDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(MemberUserDetailsService()).passwordEncoder(passwordEncoder());
}
f. 기존 login() 함수 삭제
MemberController.java
g. name을 Spring Security의 Default 값으로 변경
loginForm.jsp
name="memID"
→name="username"
name="memPassword"
→name="password"
a. 세션 → ?error
- 기존에는 세션에 로그인 정보가 있엇지만 지금은 Security로 인해 세션에 없다. (Security Context에 있음.)
- 모달창을 수정해줘야함.
// url 뒤에 ?error 확인
if(${param.error != null}) {
$("#messageType").attr("class", "modal-content panel-warning");
$(".modal-body").text("아이디와 비밀번호를 확인하세요");
$(".modal-title").text("실패메시지");
$("#myMessage").modal("show");
}
b. 세션에 로그인 정보 없으면 어떻게 활용하나.
- Security 태그 라이브러리 선언
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
c. Spring Security에서 제공하는 계정정보 (SecurityContext 안에 계정정보 가져오기)
- mvo에 계정 정보 담기
<c:set var="mvo" value="${SPRING_SECURITY_CONTEXT.authentication.principal}"/>
- auth에 권한 정보 담기
<c:set var="auth" value="${SPRING_SECURITY_CONTEXT.authentication.authorities}"/>
d. security:authorize 태그 사용
- 인증 안했으면 태그 안을 실행
<security:authorize access="isAnonymous()">
...
</security:authorize>
- 인증 했으면 태그 안을 실행
<security:authorize access="isAuthenticated()">
...
</security:authorize>
e. mvo를 사용하기 위해선..
mvo.memProfile
→mvo.member.memProfile
- mvo의 member의 memProfile, 이런식으로 한단계 더 거쳐야 가져올 수 있다.
f. 권한을 태그를 활용해 가져오기
<security:authorize access="hasRole('ROLE_USER')">
U
</security:authorize>
<security:authorize access="hasRole('ROLE_MANAGER')">
M
</security:authorize>
<security:authorize access="hasRole('ROLE_ADMIN')">
A
</security:authorize>
a. 로그아웃
- 더 이상 세션에 로그인 정보가 저장되어 있지 않다.
- session.invalidate();로 로그아웃되는 것처럼 보이지만 그렇지 않다.
- 세션으로 로그인 하는 것은 키로
jsession 아이디 쿠키
를 받는다. 마찬가지로 - Security에서 키로
세션
을 받고, 이를 해제해서 로그아웃이 된 것 처럼 보이는 것이다.- 해결 방법 : http.logout()
a-1. <button onclick="logout()">
== <a href="javascript:logout()">
a-2. 비동기 통신으로 SecurityConfig.java에 있는 http.logout() 기능으로 요청
header.jsp
- post 방식으로 보내주어야 한다.
function logout() {
$.ajax({
url : "${contextPath}/logout",
type : "post",
beforeSend : function(xhr) {
xhr.setRequestHeader(csrfHeaderName, csrfTokenValue);
},
success : function() {
location.href = "${contextPath}/";
},5
error : function() {
alert("error");
}
});
}
b. 회원 정보 수정
MemberController.java
- Authentication 객체 : 회원의 정보가 담겨있다.
- 자동완성 조심 : security.core.Authentication
- authentication.getPrincipal(); : return 타입은 User이다. 다운 캐스팅 필요
- 세션에 담는 일은 전부 다음 코드로 대체된다고 생각하면 됨.
import org.springframework.security.core.Authentication;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
MemberUser userAccount = (MemberUser) authentication.getPrincipal();
Authentication newAuthentication =
createNewAuthentication(authentication, userAccount.getMember().getMemID());
SecurityContextHolder.getContext().setAuthentication(newAuthentication);
- createNewAuthentication()이란 함수를 생성하고,
- 회원 정보를 새로 받아 재로그인해준다.
private Authentication createNewAuthentication(Authentication currentAuth, String username) {
UserDetails newPrincipal = memberUserDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken newAuth =
new UsernamePasswordAuthenticationToken(
newPrincipal, currentAuth.getCredentials(), newPrincipal.getAuthorities());
newAuth.setDetails(currentAuth.getDetails());
return newAuth;
}
c. 프로필 사진 변경
imageForm.jsp
- 기존 방식은 id를 RequestGetParameter로 가져왔지만,
- 지금은 Security의 보안 때문에 id 정보를 받아올 수 없다.
- input:hidden으로 아이디를 multi 객체에 보내고 거기서 다시 가져오는 것과 같다.
- Session 대신 Authentication을 사용하면 된다.
SpringMVC07
- MVC 패턴에 서비스 층이 추가됨
- 서비스 층은 실질적인 업무를 담당한다.
kr.spring.service
패키지 생성.
- kr.spring.service
- BoardService.java (인터페이스)
- BaordServiceImpl.java (클래스) 기능구현
- servlet-context.xml
- service 패키지를 매핑해줘야 한다.
<context:component-scan base-package="kr.spring.service" />
- 글과 댓글을 테이블 하나에 전부 표시할 수 있다.
삭제여부 | 댓글단계 | 댓글순서 | 보드그룹 | 보드번호 | 제목 |
---|---|---|---|---|---|
0 | 0 | 0 | 2 | 1 | 삭제된 게시글입니다. |
1 | 1 | 1 | 2 | 2 | ㄴ 아 오늘 아침 쌀쌀하던데? |
1 | 1 | 2 | 2 | 3 | ㄴ 맞아맞아 오늘 날씨 쌀쌀함 |
1 | 2 | 3 | 2 | 4 | ㄴ 이분 쌀쌀하다해놓고 반팔입고옵 |
1 | 0 | 0 | 1 | 5 | 어제 저으로 골뱅이 탕 먹었는데 괜찮음 ㅎㅎ |
1 | 1 | 1 | 1 | 6 | ㄴ 거짓말치지마세요!! |
1 | 2 | 2 | 1 | 7 | ㄴ 무슨 사인데 이렇게 잘 암? |
-
버전 다운그레이드
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
-
log4j 업그레이드
<groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version>
-
dependency 추가
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework-version}</version> </dependency>
-
src.test.java.kr.spring.mapper.DataSourceTest.java
- Autowired로 객체를 가져오고 @Test 어노테이션을 달고 junit 테스트를 진행하면 된다.
- 날짜 포맷을 지정할 수 있는 태그 라이브러리
- import
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
- 사용
<fmt:formatDate value="${vo.indate}" pattern="yyyy-MM-dd"/>
- SELECT INULL의 문법이 보기 어려우므로 selectKey 문법으로 별칭을 달아줄 수 있다.
<insert id="insertSelectKey" parameterType="kr.spring.entity.Board">
<selectKey keyProperty="idx,boardGroup" resultType="kr.spring.entity.Board" order="BEFORE">
SELECT IFNULL(MAX(IDX) + 1, 1) as idx,
IFNULL(MAX(BOARDGROUP) + 1, 1) as boardGroup
FROM TBLBOARD
</selectKey>
...
</insert>
SpringMVC07 답글기능 추가
<c:out value="${vo.title}"></c:out>
: XSS 방어를 위해 사
Tags | Description |
---|---|
c:out | <% = ... %> 태그 작동 방식과 유사한 표현식의 결과를 표시 |
c:import | 상대 또는 절대 URL을 검색하여 내용을 'var'의 문자열, 'varReader'의 Reader 또는 페이지에 표시 |
c:set | 평가중인 표현식의 결과를 'scope'변수에 설정 |
c:remove | 특정 범위에서 지정된 범위 변수를 제거하는 데 사용 |
c:catch | 본문에서 발생하는 Throwable 예외를 포착하는 데 사용 |
c:if | 조건을 테스트하는 데 사용되는 조건부 태그이며 표현식이 참인 경우에만 본문 내용을 표시 |
c:choose, c:when, c:otherwise | 평가 된 조건이 true 인 경우 본문 내용을 포함하는 간단한 조건부 태그 |
c:forEach | 기본 반복 태그, 고정 된 횟수 또는 초과 수집 동안 중첩 된 본문 내용을 반복 |
c:forTokens | 제공된 델리 미터로 분리 된 토큰을 반복 |
c:param | 포함하는 'import'태그의 URL에 매개 변수를 추가 |
c:redirect | 브라우저를 새 URL로 리디렉션하고 컨텍스트 기준 URL을 지원 |
c | url |
- a태그 → submit태그
- get.jsp
- 버튼에 data-btn 속성 달아주기
<button data-btn="reply">
<button data-btn="modify">
<button data-btn="list">
- 자바스크립트로 버튼을 클릭했을 때 액션 추가
<script type="text/javascript">
// 링크처리
$(document).ready(function(){
$("button").on("click", function(e){
var formData = $("#frm");
var btn = $(this).data("btn");
});
});
</script>
SpringMVC07 → SpringMVC08_Paging
- 페이징 기본 설정 Class
SpringMVC08_Paging
- pageStart ~ perPageNum
LIMIT #{pageStart}, #{perPageNum}
-
모델 정보 자동 갱신
// 다음과 같이 쓰면 modelAttribute 바로 갱신 가능.
@ModelAttribute("cri") Criteria cri
분류 | Annotation | 설명 |
---|---|---|
Spring Core | ||
@Component | 개발자가 생성한 Class를 Spring의 Bean으로 등록하기 위한 Annotation. | |
@ComponentScan | Bean을 등록 될 클래스들을 스캔하여 Bean으로 등록해줍니다. | |
@Bean | 제어가 불가능한 외부 라이브러리 등을 Bean으로 만들 때 사용. | |
@Autowired | Spring에서 Bean 객체를 주입받기 위한 Annotation. | |
Spring MVC | ||
@Controller | 해당 Class가 Controller의 역할임을 명시하기 위한 Annotation. | |
@RequestHeader | Request의 header값을 가져올 수 있는 Annotation. | |
@RequestMapping | URI의 요청과 Annotation value 값이 일치하면 실행. | |
@RequestParam | URL의 파라미터를 메소드 인자와 매칭. | |
@RequestBody | Body의 데이터를 메소드 인자와 매칭. | |
@ModelAttribute | HTTP parameter, Body 내용을 객체에 바인딩. | |
@ResponseBody | 메소드에서 리턴된 값이 View에 출력되지 않고 HTTP Response Body에 작성. | |
@GetMapping | RequestMapping(Method=RequestMethod.GET)과 동일 역할. | |
@PostMapping | RequestMapping(Method=RequestMethod.POST)과 동일 역할. | |
Spring Boot Test | ||
@SpringBootTest | Spring Boot Test에 필요한 의존성 제공. | |
@Test | JUnit에서 테스트 대상을 표시. | |
Lombok | ||
@Setter | Class의 모든 필드에 대한 Setter method 생성. | |
@Getter | Class의 모든 필드에 대한 Getter method 생성. | |
@AllArgsConstructor | 모든 필드 값을 파라미터로 받는 생성자 추가. | |
@NoArgsConstructor | 기본 생성자 자동 추가. | |
@ToString | Class의 모든 필드에 대한 toString method 생성. |
SpringMVC08_Paging
SpringMVC09_Search
- 페이징 유지
- 매개변수로 값 받기
public String 함수(Criteria cri, RedirectAttributes rttr){}
- 페이징 값 유지
rttr.addAttribute("page", cri.getPage());
rttr.addAttribute("perPageNum", cri.getPerPageNum());
SpringMVC08_Paging 복사, SpringMVC09_Search 붙여기
- sql의 JSTL (동적 sql)
- 'search'로 묶기 : sql
<sql id="search" >
<if test="type=='writer'">
where writer like concat('%', #{keyword}, '%')
</if>
<if test="type=='title'">
where title like concat('%', #{keyword}, '%')
</if>
<if test="type=='content'">
where content like concat('%', #{keyword}, '%')
</if>
</sql>
- 묶은거 사용 : include
SELECT * FROM TBLBOARD
<include refid="search" />
ORDER BY BOARDGROUP DESC, BOARDSEQUENCE ASC
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
- 스프링 부트 프로젝트 만들기
- Tomcat 실행 중지
- Project Explorer - 우클릭 - New - Other - Spring Starter Project
- https://start.spring.io 접속 : 웹에서 편리하게 프로젝트 만들 수 있다.
-
SpringMVC10_SpringInit 생성
Key Value Type Maven Packaging Jar Java Version 8 Package kr.spring Spring Boot Version 3.1.4 -
원하는 API 체크해서 넣어주기
Category Details Developer Tools Lombok, Spring Boot DevTools Web Spring Web SQL 추후 학습 예정 -
Spring Boot는 내장 Tomcat이 존재해서, Tomcat에 등록할 필요가 없다.
-
Build Path - Java version : Workspace default JRE (자바 버전을 Workspace에 맞추겠다)
-
resources
Folder Details Static css, js Templates HTML application.properties 웹 애플리케이션의 환경 설정 파일. .yaml로 변환 가능하여 사용하기도 한다. -
application.properties - 우클릭- Resource - Other - UTF-8 (한글깨짐 방지)
-
application.properties - 우클릭 - Open With - Generic Editor (Properties에서 자동완성 가능하게 함)
server.port=8081 server.servlet.context-path=/boot
-
pom.xml 버전 수정 - 프로젝트 클릭 - alt + F5 - Force Update of Snapshots/Releases
- Spring boot version : 2.7.3
- java version : 1.8
- pom.xml - 우클릭 - Spring - Add Starter
- MySQL Driver, Spring Data JPA - pom.xml 체크
- MySQL 추가할 때 버그 있다. groupID : mysql, artifactId : mysql-connector-java 로 고쳐줄 것
-
SpringMvc10BootInitApplication - 우클릭 - run as - 2 Spring boot App - 주소 직접 입력해서 접속하면 됨
-
Port 8081 was already in use. Error : 서버 중지 - 다시 실행
-
application.properties, MySQL 설정
# MySQL 추가 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/com spring.datasource.username=com spring.datasource.password=com01
-
application.properties, JPA*Hiberbate 설정
# JPA 설정 spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect # 스키마 생성 (create, update) spring.jpa.hibernate.ddl-auto=create # JPA가 실행하고 실행된 SQL문장 보기 spring.jpa.show-sql=true # 실제 JPA 구현체인 Hibernate가 동작하면서 발생하는 SQL을 포멧팅해서 출력 spring.jpa.properties.hibernate.format_sql=true
-
- SQL 없이 테이블 생성하기
카테고리 | 어노테이션 | 설명 |
---|---|---|
기본 어노테이션 | @Entity |
클래스가 엔티티임을 지정 |
@Table |
엔티티가 매핑될 테이블 정보 지정 | |
@Id |
기본 키 필드를 지정 | |
@GeneratedValue |
기본 키 생성 전략을 지정 | |
열 매핑 어노테이션 | @Column |
필드를 데이터베이스 컬럼에 매핑 |
@Transient |
해당 필드가 데이터베이스 컬럼에 매핑되지 않게 지정 | |
@Enumerated |
enum 타입을 컬럼에 매핑 |
|
@Temporal |
날짜/시간 타입을 매핑 | |
@Lob |
큰 객체나 문자열 매핑 (BLOB , CLOB ) |
|
관계 매핑 어노테이션 | @OneToOne |
일대일 관계 매핑 |
@OneToMany |
일대다 관계 매핑 | |
@ManyToOne |
다대일 관계 매핑 | |
@ManyToMany |
다대다 관계 매핑 | |
@JoinColumn |
외래 키 컬럼 지정 | |
@JoinTable |
다대다 관계의 연결 테이블 지정 | |
상속 매핑 어노테이션 | @Inheritance |
상속 전략 지정 |
@DiscriminatorColumn |
상속 테이블 간 구분 컬럼 지정 | |
@DiscriminatorValue |
구분 컬럼의 값 지정 | |
기타 어노테이션 | @Embedded |
내장 타입 사용 지정 |
@Embeddable |
값 타입을 내장 타입으로 정의 | |
@AttributeOverride |
내장 타입의 속성 재정의 | |
@SecondaryTable |
엔티티를 두 개 이상의 테이블에 매핑 | |
라이프사이클 콜백 어노테이션 | @PrePersist 등 |
엔티티 라이프사이클 이벤트에 대한 콜백 정의 |
작업 | 메소드 | 설명 |
---|---|---|
Select | find() |
단일 엔터티 검색 |
findAll() |
모든 엔터티 검색 | |
findById() |
주어진 ID를 가진 엔터티 검색 | |
Insert | save() |
새 엔터티 저장 또는 업데이트 |
Delete | deleteById() |
주어진 ID를 가진 엔터티 삭제 |
Update | save() |
이미 존재하는 엔터티의 정보 업데이트 (또는 삽입) |
- 테이블 생성을 마쳤으면, spring.jpa.hibernate.ddl-auto=update로 지정
- 이제 더이상 테이블을 지우고 생성하지 않음.
- kr.spring.repository
- JpaRepository를 상속 받아야 한다.
<table 이름/primary key>
@Repository // @Mapper 랑 같음 (생략 가능)
public interface BoardRepository extends JpaRepository<Board, Long>{}
- BoardRepository == MyBatis Mapper과 동일한 기능. 사용하려면 @Autowired로 사용하면된다.
// 다른 클래스
@Autowired
private BoardRepository boardRepository;
- kr.spring.service
Type | Name | Annotation |
---|---|---|
Interface | BoardService | |
Class | BoardServiceImpl | @Service |
- JPA 내장 함수 사용 (SQL문 없이 DB 기능 사용)
List<Board> list = boardRepository.findAll();
public Board get(Long idx);
boardRepository.save(vo);
- application.properties
spring.mvc.view.prefix=/WEB-INF/board/
spring.mvc.view.suffix=.jsp
- pom.xml
- jstl 설치
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
- jsp 설치
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
- row - col-log-{num}
- 원하는 크기만큼 화면 분할 가능
<div class="row">
<div class="col-lg-2"></div>
<div class="col-lg-5"></div>
<div class="col-lg-5"></div>
</div>
- pom.xml - 우클릭 - spring - security - spring security - pom.xml 추가 - 완료
- spring-security-taglibs 추가
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
- Spring Security에서는 반드시 username, password, role를 지정해줘야한다.
- MemberRepository 생성
@Id
private String username;
private String password;
@Enumerated(EnumType.STRING)
private Role role;
- /WEB-IBF/board만 ViewResolver면 안되므로, application.properties에서
- spring.mvc.view.prefix=/WEB-INF/board/를 다음과 같이 수정해준다.
spring.mvc.view.prefix=/WEB-INF/
- Role Entity - Enum
- enumeration, 상수 저장 집함
public enum Role {
ADMIN, MANAGER, MEMBER;
}
- CustomUser Entity
- Spring Context Holder에 Member Entity 정보를 담기 위해선 Spring Security에서 제공하는 User 엔티티의 형식으로 바꿔줄 필요가 있음.
- Member → CustomUser → User → Spring Context Holder
- Spring context Holder: 보안 금고
- kr.spring.config
- UserDetailsServiceImpl
- UserDetailsService 기능 실행
- Security 환경설정
- login.jsp - 부트스트랩 로그인페이지
- /resources/ 매핑
spring.mvc.static-path-pattern=/resources/**
-
CustomUser - getter, setter 매서드 추가
@Data
-
security 태그 추가
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
- CustomUser 객체에 접근해 member 정보 가져오기
- security 태그로 가져온다.
<sec:authentication property="principal.member.name"/>
<sec:authentication property="principal.member.role"/>
<sec:authorize>
<sec:authorize access="hasRole('ADMIN')">
내용을 작성하세요
</sec:authorize>
c:set
<!-- 로그인한 계정정보 -->
<c:set var="user" value="${SPRING_SECURITY_CONTEXT.authentication.principal}" />
<!-- 권한정보 -->
<c:set var="auth" value="${SPRING_SECURITY_CONTEXT.authentication.authorities}" />