#잡담
view Template
1순위 Thymeleaf
연관관계 연습
게시글에 좋아요, 즐겨찾기 만들기
1명의 사용자가 지금까지 좋아요 또는 즐겨찾기 한 게시물 목록 보여주기
★복습
1. CRUD (작성, 조회, 수정, 삭제)
2. 비밀번호 암호화(2가지 방식- 기본자바, 스프링 시큐리티)
ㄴ (7.수.1004에는 없음)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
dependency 에 깔고
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Config 추가했었다..
★학습목표
1. 비밀번호 암호화
2. 댓글 작성 및 조회
3. AOP
4. Filter / interceptor
★ 학습
1. 암호화 연습
# 암호화는 가능한데 복호화는 안된다.
@Autowired
PasswordEncoder passwordEncoder;
# pw 에 int 를 쓰면 null이라는 값을 못 넣기에 @Column()에 nullable 을 사용해서 오류를 해결할 수 있다
@Entity
@Data
public class Owner {
@Id
int id;
String name;
String pwd;.
이렇게 해주주면
이제 암호화 코드를 입력
passwordEncoder. encode 와 matches 를 자주 쓴다.
@PostMapping("/signup")
public String signupPost(@ModelAttribute Owner owner) {
System.out.println(owner);
String newPwd = passwordEncoder.encode(owner.getPwd());
owner.setPwd(newPwd);
ownerRepository.save(owner);
return "redirect:/auth/signup";
그러면 h2에 암호화된 코드가 pwd에 들어가게 된다
passwordEncoder -> matches()를 이용해서 비교
패스워드는 시간에 따라서 매번 다르기에 ==으로 비교 불가
기존의 알고리즘을 수정해서 matches() 이용하게 바꿔야함
id를 이용해서 findById() 이용하고 boolean 으로 맞는지 확인
@PostMapping("/signin")
public String signinPost(@ModelAttribute Owner owner) {
int id = owner.getId();
// List<Owner> result = ownerRepository.findByIdAndName(id, name);
Optional<Owner> result = ownerRepository.findById(id);
String pwd = result.get().getPwd();
// ㄴ h2에 있는 pwd를 의미함
String name = result.get().getName();
boolean isMatch =
passwordEncoder.matches(owner.getPwd(), pwd);
// ㄴ 변환전 ㄴ 변환후
if (isMatch) {
session.setAttribute("id", id);
session.setAttribute("name", name);
} else {
return "redirect:/auth/signin";
}
return "redirect:/html/emp";
public String signinPost(@ModelAttribute Owner owner) {
id 입력시에 @ModelAttribute에 문자가 오면 오류가 날수 있음 조금 보안해준다면
@PostMapping("/signin")
public String signinPost(@ModelAttribute Owner owner) {
// ㄴ숫자가 아닌 문자가 입력되었을 때 문제가 생길 수 있음
int id = owner.getId();
// List<Owner> result = ownerRepository.findByIdAndName(id, name);
Optional<Owner> result = ownerRepository.findById(id);
boolean isPresen = result.isPresent();
if (isPresen) {
String pwd = result.get().getPwd();
// ㄴ h2에 있는 pwd를 의미함
String name = result.get().getName();
boolean isMatch = passwordEncoder.matches(owner.getPwd(), pwd);
// ㄴ 변환전 ㄴ 변환후
if (isMatch) {
session.setAttribute("id", id);
session.setAttribute("name", name);
}
}
boolean으로 보충이 가능하다
<form action="/auth/signin" method="post">
<input type="text" name="id" placeholder="ID를 입력해주세요">
<br>
<input type="password" name="pwd" placeholder="pw를 입력해주세요">
<br>
<button>로그인</button>
</form>
2. 자바 기본 패키지
@Component
public class Encrypt {
public String encode(String raw) {
String hex = null;
try {
// String raw = "password1234";
MessageDigest md;
md = MessageDigest.getInstance("SHA-256");
md.update(raw.getBytes());
hex = String.format("%064x", new BigInteger(1, md.digest()));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return hex;
}
}
@Autowired
Encrypt encrypt;
불러와
@GetMapping("/signin")
public String signin() throws NoSuchAlgorithmException {
String pwd = passwordEncoder.encode("1");
System.out.println(pwd);
// ㄴ 스프링부트
String pwd2 = encrypt.encode("1");
System.out.println(pwd2);
// ㄴ 기본패키지
$2a$10$f1.BrBkAt27w0z82/BtpuO3U/IqKgqvU0UkywtFLMqKqRVeBiOAZi /// 스프링부트
6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b /// 기본패키지
ㄴ출력값
$2a$10$AaKmA.v.2ykUeR/sCVKjbea2XKrZyuaQax3PmXHi/ovXcW0WoR9Pq
6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
두번 출력했을 때 스프링부트는 바뀌지만 기본패키지는 안 바뀜
6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
눈 아픔 ㅠㅠ
2. 연관관계 연습(댓글 작성 및 조회)
1) 데이터베이스 // @Entity @Repository
ㄴ 내용, 작성자, 게시글 아이디(★프라이머리 키-연관관계), 작성일시,
ㄴ @Many To One
@Entity @Data
public class Comment {
@Id @GeneratedValue
int id;
String content;
String writer;
Date creDate; // 언더스코어로 바뀜
@ManyToOne Board board; board_id 프라이머리 키
짜잔
<form action="/board/comment" method="post">
<input type="text" name="content" >
<button>입력</button>
</form>
html에 content 해결
@PostMapping("/board/comment")
public String comment(@ModelAttribute Comment comment) {
String name = (String) session.getAttribute("name");
if(name == null) {
name= "Anonymous";
}
comment.setWriter(name);
comment.setCreDate((new Date()));
return "redirect:/board/comment";
}
writer와 date 해결 이제 board 해결해야한다.
<form action="/board/comment" method="post">
<input type="text" name="content" >
<input type="hidden" name="boardId" th:value="${board.id}">
<!-- ㄴ 이녀석은 requestParam으로 처리 -->
<button>입력</button>
@PostMapping("/comment")
public String comment(
@ModelAttribute Comment comment,
@RequestParam int boardId) <<<추가 {
String name = (String) session.getAttribute("name");
if(name == null) {
name= "Anonymous";
}
comment.setWriter(name);
comment.setCreDate((new Date()));
Board board = new Board(); <<<추가
board.setId(boardId);
comment.setBoard(board);
commentRepository.save(comment);
return "redirect:/board/lsit";
}
이렇게 코드를 작성해주면 어느정도 완성
상세보기에 게시글 작성하는 칸이 생기고 입력하게되면
h2 comment에 들어오게된다
이제 연관관계를 연습하기위해 양방향을 쓴다
Board.java에 @One To Many 사용
@OneToMany(mappedBy = "board")
List<Comment> Comments = new ArrayList<>();
<ul th:each="comment : ${board.comments}">
<li>[[${comment.content}]][[${comment.writer}]][[${comment.creDate}]]</li>
</ul>
반복문 돌리면~
밑에 li가 찍혀나온당
# 댓글 작성 시 게시물 객체를 사용했듯이
게시물 작성 시 회원 객체를 사용해야 됨
회원을 new 시킴 ex) new owner
해서 같이 넣어줘야함!
댓글 삭제하기
Controller
@GetMapping("/comment/remove")
public String commentRemove(@ModelAttribute Comment comment) {
//1번 new Commnet(), setId()
//2번 @ModelAttribute Comment comment
commentRepository.delete(comment);
return "redirect:/board/list";
}
버튼 수정
<ul th:each="comment : ${board.comments}">
[[${comment.content}]]/[[${comment.writer}]]/[[${comment.creDate}]]
<button th:onclick=" ' removeComment( ' + ${comment.id} + ' ) ' "
>삭제</button>
</ul>
스크립트 수정
function removeComment(id) {
const isOk = confirm('댓글을 삭제하시겠습니까?');
if (isOk) {
location = `/board/comment/remove?id=${id}`;
}
}
이렇게 개발자도구를 확인해서 id를 확인할 수 있다
삭제하면 h2에서도 삭제가 되는걸 볼 수 있다.
# 아까전에 리스트목록에서 댓글 수를 알 수 있게 설정한게 있다
list.html에서 수정하면
<ul th:each="board : ${boardList}">
<li>
<!-- th:href !! 꼭 th: 를 입력해야 오류가 없음 -->
<a th:href="@{/board/detail(id=${board.id})}">
[[ ${board.id} ]]
/ [[ ${board.title} ]]
/ [[${board.writer}]]
( [[ ${#lists.size(board.comments)} ]] )
</a>
</li>
</ul>
Board.java랑 Comment.java를 양방향으로 만들었기에 가능하다.
Comment.java
@Entity @Data
public class Comment {
@Id @GeneratedValue
int id;
String content;
String writer;
Date creDate;
@ManyToOne Board board;
}
Board.java
@Entity
@Data
public class Board {
@Id @GeneratedValue
// id 없으면 프라이머리 키가 없어서 작동안하고
// @GeneratedValue 순서
int id;
String title;
String content;
String writer;
@OneToMany(mappedBy = "board")
List<Comment> comments = new ArrayList<>();
}
짜잔
3. AOP - Aspect Oriented Programming #객체지향의 끝판왕?
공통적으로 적용될 모듈을 만든 후 적용하고자 하는 부분의 코드 밖에서 삽입하는 방법
사용 분야
- 메소드의 성능 테스트 - 예외 반환
- 트랜잭션 처리 - 로깅, 인증, 권한 처리 등
제일 많은 일을 하는건 AOP <<제일 강력한?
주로 프로그램을 만들면서 작성하는건 Interceptor
AOP 용어
1.Advice : 부가 기능을 담은 모듈 (메소드) /
+메소드가 동작할 때 어드바이스가 활성
2.Pointcut : Joinpoint 중 실제 Advice가 적용되는 지점 (위치+시점 지정)
execution을 pointcut이라고 함.
3.Aspect : Advisor의 집합 (Advice + Pointcut)
*은 모드 타입
(* com.example.basic.controller.*(모든클래스).*(모든메소드)(..))")
@Component < Bean 등록
@Aspect < AOP 기능 활성
... AOP는 모든 메소드를 다 활용가능
Filter는 AOP보다 할 수 있는 일이 적음
오로지 url에 대해서만 //으로 접속하는 url에만 동작을 함.
로그를 남기거나 검증해보고 싶을때
웹브라우저 <>서버 (a,<>b <>c <>d )
ㄴ 스프링이라는 위치는 d . tomcat은 ( )
filter는 스프링, AOP, Interceptor보다 먼저 동작함
요청과 응답에 불필요하거나 보여주기 싫은 또는 받기 싫은것들을 거를 수 있음
Interceptor도 url만 가능
일단 스프링 안으로 들어온 이후에 인터셉터가 가로채서 동작 AOP는 그 이후에 작동
AOP는 요청 뿐만 아니라 세부적으로 작업가능
- preHandler() : Controller의 메소드가 실행되기 전 (요청)
- postHandler() : Controller의 메소드가 실행된 후 (응답)
이해가 잘 안됨 ㅠ 우선 여기까지 하고 저녁에 공부해서 올릴게유..