
1. 멀티 스레드 VS 단일 스레드
1) 멀티 스레드
- request마다 스레드를 만드는 기법
- 컨텍스트 스위칭이 발생 → 손님이 기다리는 시간은 적으나 전체적인 시간은 느림
- 그림을 동시에 그리거나 할 때 유용함
- IO가 있는 업무 : 스레드들 끼리 웨이트가 걸림
2) 단일 스레드
- 일단 request를 다 받고 이벤트 루프에 다 등록한 이후 틈이 생기면 이벤트 실행, 돌아가고 함
- 컨텍스트 스위칭이 없음
- IO가 있는 업무 : request를 다 받고 동시에 요청함 → 제일 빠름
⇒ 톰캣을 멀티 스레드로 배웠으나 웹에서는 단일 스레드가 최고임
톰캣도 단일과 멀티 스레드 둘 다 제공함
2. Javascript 사용의 장점
- 로그인이 취소됐을 때 redirection이 되면 내용이 다 사라짐
→ 자바스크립트를 쓰면 히스토리 백이 되서 내용이 남아있어 ux가 좋아짐
- 권한이 없으면 alert창 띄워서 권한이 없습니다 하고 히스토리 백해서 redirection없이 자동 이동
3. User에서 username에 unique 제약 설정하기
package shop.mtcoding.blog.user;
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data //GETTER, SETTER, toString 포함된 어노테이션
@Entity // DB의 테이블과 매핑되는 클래스 임을 알려줌
@Table(name = "user_tb") //테이블 이름 : user_tb
public class User { // use_tb의 내용 DB에 담기
@Id // PK
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment
private Integer id;
@Column(unique=true) // 유니크 설정
private String username;
@Column(length = 60, nullable = false) // 길이 조정, 널 불가
private String password;
private String email;
// 자바 : 카멜 표기법 -> DB : _ (언더스코어 기법)
private LocalDateTime createdAt; // created_at
}
4. UserController 에서 /join주소 수정하기
- 오류 메세지를 직접 리턴하게 수정하기
- @Controller // 파일을 리턴함 -> @ResponseBody로 메세지 자체를 리턴
- 뷰 리졸버가 동작하지 않음 → 메세지 그대로 리턴
package shop.mtcoding.blog.user;
import jakarta.servlet.http.HttpSession;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
@AllArgsConstructor
@Controller // 파일을 리턴함 -> @ResponseBody로 메세지 자체를 리턴
public class UserController {
// fianl 변수는 반드시 초기화 되어야 함
private final UserRepository userRepository; // null
private final HttpSession session;
// @AllArgsConstructor를 사용하면서 필요 없어짐
// public UserController(UserRepository userRepository, HttpSession session) {
// this.userRepository = userRepository;
// this.session = session;
// }
// 원래는 get요청이나 예외 post요청하면 됨
// 민감한 정보는 쿼리 스트링에 담아보낼 수 없음
//원래는 get요청이나 예외 post요청하면 됨
//민감한 정보는 쿼리 스트링에 담아보낼 수 없음
@PostMapping("/login")
public String login(UserRequest.LoginDTO requestDTO) {
// 1. 유효성 검사
if(requestDTO.getUsername().length() < 3) {
return "error/400";
}
// 2. 모델 필요 select * from user_tb where username=? and password=?
User user = userRepository.findByUsernameAndPassword(requestDTO); // DB에 조회할때 필요하니까 데이터를 받음
if (user == null) {
return "error/401";
}else {
session.setAttribute("sessionUser", user);
return "redirect:/";
}
}
@PostMapping("/join")
public @ResponseBody String join(UserRequest.JoinDTO requestDTO) {
System.out.println(requestDTO);
try{
userRepository.save(requestDTO);
} catch (Exception e) {
return "error/400";
}
return "redirect:/loginForm"; //리다이렉션불러놓은게 있어서 다시부른거
}
@GetMapping("/joinForm") // view만 원함
public String joinForm() {
return "user/joinForm";
}
@GetMapping("/loginForm") // view만 원함
public String loginForm() {
return "user/loginForm";
}
@GetMapping("/user/updateForm")
public String updateForm() {
return "user/updateForm";
}
@GetMapping("/logout")
public String logout() {
// 1번 서랍에 있는 uset를 삭제해야 로그아웃이 됨
session.invalidate(); // 서랍의 내용 삭제
return "redirect:/";
}
}


5. _core.util 패키지 만들어서 Script 클래스 만들기
package shop.mtcoding.blog._core.util;
public class Script {
public static String back(String msg) {
StringBuilder sb = new StringBuilder();
sb.append("<script>");
sb.append("alert('"+msg+"');");
sb.append("history.back();");
sb.append("</script>");
return sb.toString();
}
public static String href(String path) {
StringBuilder sb = new StringBuilder();
sb.append("<script>");
sb.append("location.href='"+path+"';");
sb.append("</script>");
return sb.toString();
}
public static String href(String path, String msg) {
StringBuilder sb = new StringBuilder();
sb.append("<script>");
sb.append("alert('"+msg+"');");
sb.append("location.href='"+path+"';");
sb.append("</script>");
return sb.toString();
}
}
6. Junit으로 테스트 해보기
- 똑같은 패키지를 만들고 class 이름에 @Test만 붙이기
- _기법 사용
- 스프링이 STRING을 리턴하면 메세지를 그대로 리턴
- 브라우저가 랜더링 해줄 때 스크립트가 발동됨 → 자동 TEXT HTML파일로 해줌
package shop.mtcoding.blog._core.util;
import org.junit.jupiter.api.Test;
public class ScriptTest {
@Test
public void back_test(){
String result = Script.back("권한이 없어요"); // string으로 받음
System.out.println(result);
}
}
7. UserController에서 /join 수정하기
- Script.href 사용하기
메세지를 자바스크립트로 주면 302를 줄 필요 없음
package shop.mtcoding.blog.user;
import jakarta.servlet.http.HttpSession;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import shop.mtcoding.blog._core.util.Script;
@AllArgsConstructor
@Controller // 파일을 리턴함 -> @ResponseBody로 메세지 자체를 리턴
public class UserController {
// fianl 변수는 반드시 초기화 되어야 함
private final UserRepository userRepository; // null
private final HttpSession session;
// @AllArgsConstructor를 사용하면서 필요 없어짐
// public UserController(UserRepository userRepository, HttpSession session) {
// this.userRepository = userRepository;
// this.session = session;
// }
// 원래는 get요청이나 예외 post요청하면 됨
// 민감한 정보는 쿼리 스트링에 담아보낼 수 없음
//원래는 get요청이나 예외 post요청하면 됨
//민감한 정보는 쿼리 스트링에 담아보낼 수 없음
@PostMapping("/login")
public String login(UserRequest.LoginDTO requestDTO) {
// 1. 유효성 검사
if(requestDTO.getUsername().length() < 3) {
return "error/400";
}
// 2. 모델 필요 select * from user_tb where username=? and password=?
User user = userRepository.findByUsernameAndPassword(requestDTO); // DB에 조회할때 필요하니까 데이터를 받음
if (user == null) {
return "error/401";
}else {
session.setAttribute("sessionUser", user);
return "redirect:/";
}
}
@PostMapping("/join")
public @ResponseBody String join(UserRequest.JoinDTO requestDTO) {
System.out.println(requestDTO);
try{
userRepository.save(requestDTO);
} catch (Exception e) {
return Script.back("아이디가 중복되었어요");
}
return Script.href("/loginForm"); // 메세지를 자바스크립트로 주면 302를 줄 필요 없음
}
@GetMapping("/joinForm") // view만 원함
public String joinForm() {
return "user/joinForm";
}
@GetMapping("/loginForm") // view만 원함
public String loginForm() {
return "user/loginForm";
}
@GetMapping("/user/updateForm")
public String updateForm() {
return "user/updateForm";
}
@GetMapping("/logout")
public String logout() {
// 1번 서랍에 있는 uset를 삭제해야 로그아웃이 됨
session.invalidate(); // 서랍의 내용 삭제
return "redirect:/";
}
}


Share article