연구실 데이터를 CRUD하는 API 인데 아무나 접근할 수 있으면 보안상 문제가 많다고 생각
데이터를 사용하는 연구원들도 Read 이외 생성, 수정, 삭제 기능에 접근 제한을 두는 것이 데이터 관리에 있어서 중요하다고 생각
-> 데이터를 사용하는 연구원들은 Read 기능만 접근가능하게 함
-> 디비 관리자만 모든 기능 접근 가능하게 함
How?
모든 연구원들이 회원가입하여 아이디 비번을 생성하고 관리자가 슈퍼user, 일반user를 지정해줘도 되지만 개인 유저가 가지는 큰 의미가 없어 슈퍼user와 일반user 두개의 아이디만 필요하다고 생각 -> 나중에 회원가입 기능이 추가되고 개개인이 갖는 위치데이터 입력시 개인 id를 갖는게 더 유리할 수 있음
쿠키, 세션, JWT 중 JWT 사용
쿠키는 쿠키값 그대로 보내 보안의 문제
세션은 서버에 추가적인 저장소가 필요함
토큰방식은 디비 저장소도 필요없고 로그인시 암호화된 문자열을 보내 보안상 안전함
좀 더 고민해보자
서브모듈 적용하여 application.yml 파일에 있는 보안 정보(디비 정보, jwt secretKey 등) 숨김
진행
User Table
is_super_user 필드로 슈퍼user와 일반user 구분
JWT
TokenProvider
jwt 생성하고 추출하는 기능
LoginInterceptor
컨트롤러에 닿기전에 가로채 로그인이 되어 있는지(제대로된 토큰이 넘어왔는지) 확인하는 기능
AuthArgumentResolver
인터셉터 작업이 끝난 뒤 컨트롤러로 갔을때 필요한 토큰 값을 원하는 형태로 받고 싶을때 사용
Authentication 어노테이션 → 컨트롤러 아규먼트 어노테이션
CurrentUser 클래스 → 아규먼트를 담당하는 클래스
WebMvcConfig
LoginInterceptor 와 AuthArgumentResolver 를 Bean 으로 등록하고
각각 registry 와 resolvers 에 등록한다
서브모듈
보안 정보를 담을 private repository 만들고
본 프로젝트의 서브모듈로 설정함
processResources.dependsOn('~~~') 를 사용하여 원하는 위치에 파일을 복사하여 옮김
application.yml 에 있는 데이터는 @Value 어노테이션을 사용하여 가져올 수 있다
24번 반복문을 돌리면서 현재 피로도 + A ≤ M 인지 파악하면서 가능하면 일하고(+ A) 아니면 쉬게한다(- C)
일할때마다 총 일하는 크기를 더해간다(+ B)
피로도가 음수로 내려가면 0으로 바꿔준다
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int A, B, C, M;
int tiredness = 0;
int answer = 0;
A = Integer.parseInt(st.nextToken());
B = Integer.parseInt(st.nextToken());
C = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
for (int i = 0; i < 24; i++) {
if (tiredness + A <= M) {
tiredness += A;
answer += B;
} else {
tiredness -= C;
if (tiredness < 0) {
tiredness = 0;
}
}
}
System.out.println(answer);
}
}
회고
문제를 잘 읽도록 하자. 다소 쉬운 문제지만 문제를 꼼꼼히 읽지 않아서 생기는 오류가 많았다. 예를 들면 피로도가 음수일때 0으로 한다, 피로도가 M을 넘기면(≤) 번아웃이다.
시간초과 오류났는데 입력받는 시간이 오래걸린다고 함 Scanner -> BufferedReader 사용
Scanner는 원시타입과 String 파싱 등 정규 표현식 적용, 입력값 분할, 파싱 과정을 거치기 때문에 낮은 퍼포먼스
BufferedReader는 입력을 버퍼에 모아두었다가 한번에 그 내용을 전달하기 때문에 빠른 속도
왜 더 빠를까?
Scanner도 버퍼로 입력받는걸로 이해했는데...?
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.util.Stack;
public class Main {
static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
StackClass stackClass = new StackClass();
int totalCommandCount = Integer.parseInt(bf.readLine());
for (int i = 0; i <= totalCommandCount; i++) {
stackClass.execute(bf.readLine());
}
}
private static class StackClass {
private Stack<Integer> stack = new Stack<>();
public StackClass() {
}
public void execute(String command) {
if (command.startsWith("push")) {
String[] split = command.split(" ");
push(Integer.parseInt(split[1]));
}
if (command.equals("pop")) {
pop();
}
if (command.equals("size")) {
size();
}
if (command.equals("empty")) {
empty();
}
if (command.equals("top")) {
top();
}
}
private void push(int x){
stack.push(x);
}
private void pop() {
if (stack.isEmpty()) {
System.out.println(-1);
return;
}
System.out.println(stack.pop());
}
private void size() {
System.out.println(stack.size());
}
private void empty() {
if (stack.isEmpty()) {
System.out.println(1);
return;
}
System.out.println(0);
}
private void top() {
if (stack.isEmpty()) {
System.out.println(-1);
return;
}
System.out.println(stack.peek());
}
}
}
회고
이번 문제도 if문을 나열해서 풀었는데 가독성 면에서는 스위치문이 좋고 성능면에서는 else if가 좋을것 같다는 피드백을 받았다
확실히 스위치문으로 보니 가독성이 올라간다고 느껴졌다
import java.io.*;
import java.util.*;
public class Main {
public static void printValue(Integer value) {
System.out.println(value == null ? -1 : value);
}
public static void main(String[] args) throws IOException {
Deque<Integer> deque = new LinkedList<>();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
StringTokenizer st;
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
String command = st.nextToken();
switch (command) {
case "push_front":
deque.addFirst(Integer.parseInt(st.nextToken()));
break;
case "push_back":
deque.addLast(Integer.parseInt(st.nextToken()));
break;
case "pop_front":
printValue(deque.pollFirst());
break;
case "pop_back":
printValue(deque.pollLast());
break;
case "size":
printValue(deque.size());
break;
case "empty":
printValue(deque.isEmpty() ? 1 : 0);
break;
case "front":
printValue(deque.peekFirst());
break;
case "back":
printValue(deque.peekLast());
break;
}
}
}
}