새로운 프로젝트 시작.
kakao project 생성
- Spring Boot 2.6.8 버전 사용.
- Spring Web, Thymeleaf, Spring Boot DevTools dependency 추가
-> 의존성 추가
- ExeptionUtils와 DateUtils을 사용하기 위해 apache 추가
- controllers, mappers, services 패키지 생성
-> StandardController 추가
- Controller 생성자 만듬. 접근 권한 제어자 : protected
-> SystemService @Service 어노테이션만 추가
-> ISystemMapper 추가
- @Mapper 어노테이션 추가
-> SystemService
- final인 멤버변수 추가
-> StandardController
- ExceptionHandler에 ModelAndView를 사용해도 객체화를 해주지 않아서 수동 객체화해야한다.
-> templates패키지에 error.html 생성
- _error인 이유는 특정맵핑을 위해 존재하는 html이 아니라 공용으로 이용할 것이기 때문이다.
-> StandardController
- 오류가 handle됐더라도 우리가 어떤 오류인지 알아야 하기 때문에
exception.printStackTrace();
추가한다.final protected
추가로 더 이상 재정의 할 수 없도록 해준다.
-> RootController 추가 (HomeController로도 사용가능.)
- StandardController를 상속받는데 StandardController는 생성자가 하나밖에 없으니 상속받을 때 구현을 해주어야 한다.
**-> StandardController**
> ![](https://velog.velcdn.com/images/zirccon/post/8177df75-09d2-432e-a72f-f33fff97fd55/image.png)
-> index.html 추가
- 나중에 unsigned.html 로 변경할 것임.
- 계정이름 : 'daemom_kakao'@'localhost'
권한 : 모든 스키마 / 테이블에 모든 권한 부여
-> application.properties
스키마를 분리해서 사용할 것이다.
-> error_logs 테이블 생성
CREATE SCHEMA `kakao_system`;
CREATE TABLE `kakao_system`.`error_logs`
(
`index` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`created_at` DATETIME(6) NOT NULL DEFAULT NOW(6),
`client_ip` VARCHAR(50) NOT NULL,
`client_ua` VARCHAR(500) NOT NULL,
`request_uri` VARCHAR(500) NOT NULL,
`message` VARCHAR(1000) NOT NULL,
`stacktrace` VARCHAR(10000) NOT NULL,
CONSTRAINT PRIMARY KEY (`index`)
);
-> ErrorLogEntity 추가
private long index; private Date createdAt; private String clientIp; private String clientUa; private String requestUri; private String message; private String stacktrace;
- GetterSetter 도 추가.
- 분류로써 prefix(프리픽스)가 붙어 class의 이름이 길어지는 것을 방지하기 위해서 패키지를 분류하여 생성하자.
=SystemErrorLogEntity
->ErrorLogEntity
-> SystemService
public void putErrorLog(ErrorLogEntity errorLogEntity)
putErrorLog
메서드 생성
-> ISystemMapper
- int타입을 반환하는
insertErrorLog
추가
@SuppressWarnings("UnusedReturnValue")
넣는 방법.
어노테이션 사용 전에는insertErrorLog
가 노란색 박스로 뜨면서 spring이 void 타입을 쓰라고 경고를 주고 있는데 우리는 int로써 사용을 하고 싶기 때문에 어노테이션을 사용해서 경고를 없앤다.
-> 다시 SystemService
- ISystemMapper에 있는 메서드 호출
-> resources, mappers 패키지 생성 후 SystemMapper.xml 생성
<mapper namespace="dev.jwkim.kakao.mappers.ISystemMapper">
인터페이스의 풀네임을 namespace에 적어준다.
IFNULL(? , x)
? 자리에 들어가는 값이 null이라면 대신 뒤에 것(x)을 사용하겠다는 의미.
errorLogEntity를 객체화하는데 created_at을 NULL로 두고 insert로 호출하게 되면 그 뒤에 것 DEFLAULT( )를 사용하겠다는 의미이다.
=> 열 이름에 해당하는 전달된 열의 기본값을 사용한다.
created_at의 기본값을NOW(6)
로 지정해주었음으로
결과적으로 created_at이 NULL이면 created_at의 열에 DEFLAULT = 기본 값이 들어간다.
-> StandardController
- 예외가 터졌을 때 값을 insert 해야한다.
try catch로 모든 예외를 받아서 아무런 조치도 취해주지 않는다.
ErrorLogEntity errorLogEntity = new ErrorLogEntity();
객체화 해준다.
-> created_at을 해주지 않아도 된다는 의미이다. null로 두게 되면 현재시간이 들어가게 된다.
"User_Agent"은 null이 나올 수도 있기 때문에 따로 뺴준다.
-> 테이블 생성
CREATE TABLE `kakao_member`.`statuses`
(
`code` VARCHAR(10) NOT NULL ,
CONSTRAINT PRIMARY KEY (`code`)
);
CREATE TABLE `kakao_member`.`genders`
(
`code` TINYINT(1) UNSIGNED NOT NULL,
CONSTRAINT PRIMARY KEY (`code`)
);
CREATE TABLE `kakao_member`.`contact_companies`
(
`code` VARCHAR(5) NOT NULL ,
`name` VARCHAR(10) NOT NULL ,
CONSTRAINT PRIMARY KEY (`code`),
CONSTRAINT UNIQUE (`name`)
);
CREATE TABLE `kakao_member`.`contact_firsts`
(
`number` VARCHAR(4) NOT NULL ,
CONSTRAINT PRIMARY KEY (`number`)
);
-> users 테이블 추가
CREATE TABLE `kakao_member`.`users`
(
`email` VARCHAR(50) NOT NULL,
`password` VARCHAR(128) NOT NULL COMMENT 'SHA-512',
`created_at` DATETIME NOT NULL DEFAULT NOW(),
`status_code` VARCHAR(10) NOT NULL,
`status_at` DATETIME NOT NULL DEFAULT NOW(),
`profile_nickname` VARCHAR(10) NOT NULL,
`profile_thumbnail` MEDIUMBLOB NOT NULL,
`profile_message` VARCHAR(100) NOT NULL,
`personal_name` VARCHAR(5) NOT NULL,
`personal_birth` DATE NOT NULL,
`personal_gender_code` TINYINT(1) UNSIGNED NOT NULL,
`contact_company_code` VARCHAR(5) NOT NULL,
`contact_first_number` VARCHAR(4) NOT NULL,
`contact_second` VARCHAR(4) NOT NULL,
`contact_third` VARCHAR(4) NOT NULL,
CONSTRAINT PRIMARY KEY (`email`),
CONSTRAINT UNIQUE (`email`),
CONSTRAINT UNIQUE (`profile_nickname`),
CONSTRAINT UNIQUE (`contact_first_number`, `contact_second`, `contact_third`),
CONSTRAINT FOREIGN KEY (`status_code`) REFERENCES `kakao_member`.`statuses` (`code`)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY (`personal_gender_code`) REFERENCES `kakao_member`.`genders` (`code`)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY (`contact_company_code`) REFERENCES `kakao_member`.`contact_companies` (`code`)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY (`contact_first_number`) REFERENCES `kakao_member`.`contact_firsts` (`number`)
ON DELETE CASCADE
ON UPDATE CASCADE
);
`created_at` # 회원가입 일자
`status_code` # 회원의 상태
`status_at` # 마지막으로 회원 상태가 변경된 일시
`profile_nickname` # 별명
`profile_thumbnail` # 프로필 사진
`profile_message` # 상태 메세지
`personal_name` # 실명 / 개인정보
`personal_birth` # 생년월일 / 개인정보
`personal_gender_code` # 주민등록번호 뒷자리 첫자 / 개인정보
`contact_company_code` # KT LGU SKT
`contact_first_number` # 전화번호 첫번째자리
`contact_second` # 전화번호 두번째자리
`contact_third` # 전화번호 세번째자리
-> entitis 패키지에 member 패키지 생성, UserEntity 추가
-> sessions 담당 테이블 추가 : 중복 로그인을 막기 위해
-> SessionEntity 추가
`kakao_member`.`sessions` # 중복 로그인을 막기 위해
`created_at` # 세션 생성 일자
`expires_at` # 로그인 후 아무것도 안한 채로 1시간이 지나면 자동으로 로그아웃시키겠다.
`expired_flag` # 사용자가 로그아웃을 직접 누르면 true로 업데이트
`last_at` # 이 사람이 마지막으로 활동한 ip, uri(주소)를 추적할 것
`last_ip` # 마지막 활동 ip
`last_uri` # 마지막 활동 주
-> 테이블 열 추가
contact_companies
contact_firsts
genders
statues
: 정해준 이름으로 Model에 객체가 추가될 경우 실제로 Model이 아닌 세션에 저장한다.
: 세션 저장소에서 객체를 받아온다.
: 모델 저장소에서 객체를 받아온다.
-> StandardController
- 파란줄 추가
@ModelAttribute(name = UserEntity.ATTRIBUTE_NAME) protected UserEntity userEntity() { return null; }
- SessionAttributes 추가
-> UserEntity에 추가
public static final String ATTRIBUTE_NAME = "userEntity";
-> unsigned.html / signed.html
- 로그인 했을 때 / 로그인 하지 않았을 때 이 두개의 경우를 동일한 주소에서 다른 페이지로 보내기 위해 생성. (index.html -> unsigned.html 로 변경)
이름 두개 변경하였고 unsigned.html 로그인 내용 추가
-> RootController
- 세션 저장소에서 객체를 받아온다. SessionAttributes에서 가져오는 것.
font 링크로 추가
- link 걸어준다.
-> resources 패키지에 images 패키지 생성. 카카오톡 logo이미지 추가
-> unsigned.html
-> stylesheets 패키지에 common.css / index.css 추가
-> index.css
@charset "UTF-8";
body.unsigned {
width: 100%;
height: 100%;
background-color: rgb(var(--common-color-yellow-default));
position: absolute;
}
body.unsigned > .cover {
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 50%);
position: fixed;
}
body.unsigned > .login-form {
top: 50%;
left: 50%;
align-items: stretch;
background-color: rgb(var(--common-color-yellow-default));
border: 0.0625rem sollid rgba(var(--common-color-yellow-default), 90%);
border-radius: 0.5rem;
display: flex;
flex-direction: column;
box-shadow: 0 0 0.5rem 0.0625rem rgba(50,50,50,50%);
padding: 5rem 2.75rem;
position: fixed;
transform: translate(-50%, -50%);
z-index: 1;
}
body.unsigned > .login-form > .logo {
width: 7.5rem;
user-select: none;
-webkit-user-drag: none;
}
-> common.css
@charset "UTF-8";
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard-dynamic-subset.css');
:root {
--common-color-brown-default:60, 30, 30;
--common-color-yellow-default: 254, 229, 0;
--common-text-color-default: 24,22,0;
}
html {
color: rgb(var(--common-text-color-default));
font-family: 'Pretendard', sans-serif;
font-size: 1rem;
font-weight: 400;
}
body {
margin: unset;
}
-> 현재까지 브라우저로 보이는 결과
-> 경로 설정 (static)
- command + ;
- 누르고 해당 경로를 작성하거나 찾아서 넣으면 된다.
- 내가 설정한 경로.
- 자동완성 되는 것을 확인 할 수 있다.