CORS

CORS


CORS(Cross-Origin Resource Sharing) 직역하면 교차 출처 리소스 공유인데(심지어 리소스는 그냥 영어잖아) HTTP 헤더를 사용해서 A 출처에서 실행 중인 웹 애플리케이션이 B 출처의 리소스에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제라고 한다.
주로 프론트엔드 서버 - 백엔드 서버간 통신을 해야할 때 보안상의 이유로 설정해줘야 한다. 허락 받은 프론트엔드만 백엔드 서버의 응답을 받을 수 있다는 것.



이를 위한 Spring 설정은


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {	
    @Override
    public void addCorsMappings(CorsRegistry registry) {
    	registry.addMapping("/**")
                .allowedOrigins("http://xx.xxx.xxx.xx:port"); //프론트엔드 서버

    }
}

이렇게 설정을 해주고 만약 스프링 시큐리티를 사용한다면 WebSecurityConfigurerAdapter를 상속하는 클래스에 http.cors() 메소드를 사용해주고 위에서 만든 addCorsMappings를 override해주면 되겠다.



출처 : [Spring] 스프링의 CORS설정, SpringBoot에서 CORS할 때 header, preflight 이슈 해결하기, CORS는 왜 이렇게 우리를 힘들게 하는걸까?

ORM, SQL, MVC

ORM


ORM: Object-Relation Mapping

Object: 객체지향 언어(자바, 파이썬)
Relational: 관계형 데이터베이스(H2,MySQL)

객체와 관계형 데이터베이스의 데이터를 자동으로 연결(mapping)해주는 것



ORM이 없으면 웹서버 개발은 못 하나?


ORM이 없어도 웹서버 개발 가능은 하지만…

  • ORM이 없는 환경에서는 백엔드 개발자가 비즈니스 로직 개발보다 SQL 작성에 더 많은 시간을 할애하게 됨.
  • SQL 작성이 단순하고 반복적인데 실수하기는 쉬움
  • 그리고 웹 서버 개발 언어와 관계형 데이터베이스 언어의 목적 및 사용방법이 다름



백엔드 개발자는 DB에 대해 몰라도 되나?


그렇지 않다ㅠㅠ
DB 테이블 설계라든가 SQL Query 성능 확보 등을 위해서도 알아야 함




SQL


SQL은 관계형 데이터베이스 관리 시스템의 데이터를 관리하기 위해 설계된 특수 목적의 프로그래밍 언어다.
관계형 데이터베이스 관리 시스템에서 자료의 검색과 관리, 데이터베이스 스키마 생성과 수정, 데이터베이스 객체 접근 조정 관리를 위해 고안되었다. (출처 위키백과)

대표적인 데이터베이스 관리 시스템은 MySql이 있는데… query 매번 까먹어서 정리된 사이트 열어두고 참고하며 했던 기억이…ㅎㅎ
근데 확실히 스프링에서 JPA로 처리가 가능하니까 일일이 insert into 안 써도 되고 좋긴 하다ㅋㅋ 아직은 모르는 게 많아서 애증의 존재지만.




스프링 MVC


스프링 MVC란
Model- View - Controller 디자인 패턴


Response


server에서 html을 내려주는 경우

  1. 정적(static) 웹 페이지
    • Controller
    • Client의 요청을 Model로 받아 처리
      • 예) 회원가입을 위한 개인정보들(id, password, name)
    • Client에게 View(정적 웹페이지, html)를 내려줌
  2. 동적(dynamic) 웹 페이지
    • Controller
    • Client의 요청을 Model로 받아 처리
    • Tamplate engine에게 View, Model 전달
      • View: 동적 html 파일
      • Model: View에 적용할 정보들
    • Template engine
      • View에 Model을 적용 -> 동적 웹페이지 생성
        • 예) 로그인 성공 시, “로그인된 사용자의 ID”를 페이지에 추가
        • Template engine 종류: 타임리프(Thymeleaf), Groovy, FreeMarker, Jade 등(스프링에서 JSP 이용은 추천하지 않고 있음)
    • Client에게 View(동적 웹페이지, html)를 내려줌

사실 요즘은 json 등으로 서버와 클라이언트 간에 데이터 주고받는 게 기본이라 딱히 정적인 웹페이지 / 동적인 웹페이지 구분이 없는 듯?



Controller와 HTTP Response 메시지


Controller HTTP Response
@ResponseBody Return 타입 Return 값 Header Body
X String {View name} Content-Type:text/html View(HTML)의 text 내용(타입리프 기준) /templates/{View name}.html
redirect:/{redirect URL} Location:{Host URL}/{redirect URL} 없음
O String {Text} Content=Type:text/html {Text}
String 외 java 객체 Content-Type:application/json JSON (스프링이 java객체 -> json 변환)

HTTP메시지



스프링 MVC 동작원리


스프링으로 작업하면서 Controller가 시작점과 끝점으로 보이지만, 사실 스프링이 뒤에서 많은 부분을 처리해주고 있다는 거…


img

  1. Client -> DispatcherServlet
    • 가장 앞 단에서 요청을 받아 FrontController라고도 불림
  2. DispatcherServlet -> Controller
    • API를 처리해 줄 Controller를 찾아 요청을 전달
    • Handler mapping에는 API path와 Controller 함수가 매칭되어 있음 -> 함수 이름을 내 마음대로 설정 가능했던 이유
    • Controller에서 요청하는 Request의 정보(Model) 전달
  3. Controller -> DispatcherServlet
    • Controller가 Client로 받은 API 요청을 처리
    • Model 정보와 View 정보를 DispatcherServlet으로 전달
  4. DispatcherServlet -> Client
    • ViewResolver를 통해 View에 Model을 적용
    • View를 Client에게 응답으로 전달



Request



Controller와 HTTP Request 메시지

Controller HTTP Sample Request
Annotation 생략가능 Sample Code
@PathVariable X /{id} GET /a/b/c
@RequestParam O @GetMapping(.../param) GET /param?id=a
O @PostMapping(.../param POST /form/param Header Content type:application/x-www-form-urlencoded Body id=a
@ModelAttribute O @PostMapping("/form/model") @ModelAttribute Star star (이걸로 선언한 객체에는 @Setter 선언이 필요) POST /form/model Header Content type:application/x-www-form-urlencoded Body id=a
@RequestBody X @RequestBody Star star POST /form/json Header Content type: application/json Body {"id":"a"}

(항해99) 3주차 주특기 숙련

Spring Security


입문 주차 때 비밀번호 암호화 해보겠다고 깝치다가 spring security를 살짝 찍먹했었는데 ㅋㅋ 아아 그것은 아무것도 아니었다.

사실 스프링 시큐리티를 잘 활용하면 로그인폼/로그아웃/인증/인가 다~ 알아서 해주기 때문에 편하지만 우리는 JWT를 사용해야 했다.
무슨 말이냐? 이 스프링 시큐리티를 커스텀해서 써야 한다는 말ㅋㅋㅋ

거기서부터 지옥이 시작되었다.

일단 JWT를 웹미니 프로젝트 때 파이썬으로 경험해봐서 아 그거? 했지만
ㅋ…


강의


사실 무작정 ‘하세요’ 하고 던져지는 건 아니고 강의가 주어지기 때문에 강의를 참고해서 만들면 되지만
그렇게 쉽다면 이렇게 징징대진 않았겠지 ㅋㅋ
강의에서 분명 스프링 시큐리티로 로그인 구현을 했지만 JWT는 잠깐 스쳐지나가는 수준으로 다뤘기 때문에(ㅋㅋㅋ) JWT 할 사람은 참고하세요~ 하는 느낌으로 받은 파일은… 정말 아이고 이게 무슨 일이얔ㅋㅋㅋ
그야말로 멘탈 와르르맨션;;



Anyway

대가리 꺠져가며 ㅋㅋ 코드 뜯어보고, 일일이 test를 하면서 깨달은 부분을 정리해보자면

loginJwt

일단 이건 받은 JWT파일을 킹림판을 활용하여 ㅋㅋ 정리한 건데
JwtTokenUtils에 있는 토큰 정보, 알고리즘으로 인코딩한 시크릿키로 해당 파일의 순서로 JWT(json web token)을 생성하고~ 인증하고~ 인가하고~ 한다는 것이다.
그리고 그 대부분의 기능이 스프링 시큐리티의 인증관리자가 처리하기 때문에 WebSecurityConfigurarAdapter를 상속한 config를 만들어서 처리를 해줘야 한다는 것이다.
아 그리고 스프링 버전이 높으면 해당 어댑터 상속이 밑줄처리 되면서 사용이 안 될 수도 있으니까 버전 확인하고 프로젝트를 생성하고, 이미 만들었어도 빌드에서 수정 가능하니까 괜찮다 ㅋㅋ


힘들었던 부분


아무튼 스프링 시큐리티를 커스텀하는 게 물론 힘들었지만 사실 나는 가장 시간을 허비한 것이 ajax 였다ㅠㅠ


  1. 인가 문제

애초에 분명 authorizated 되었을 건데 로그인을 해도 해당 페이지에 접근이 거부되는 것이다ㅠㅠ
근데 알고보니 인가 확인을 request.getHeader(“Authorization”)이 null인지 아닌지로 했는데 로그인을 해도 안 해도, token이 있어도 없어도 null로 뜸ㅋㅋㅋㅋㅋ
스프링시큐리티가 접근을 막은 것인지 뭔지 아무튼 ㅠㅠ 그래서 javascript로 ㅋㅋㅋ 쿠키를 체크한 다음에 토큰이 있으면 ajax header에 해당 토큰을 넣어주고 -> ajax로는 인가가 가능해져서 직접 페이지에 접근하는 걸로는 확인할 수 없지만 ㅋㅋㅋ 막아놓은 페이지는 ajax GET으로 하면 error가 response되게 해서 ㅋㅋㅋ 못 들어가게 했다. 꼼수라면 꼼수랄까…


  1. error 처리

하 정말;; 왜 error가 콘솔에서는 뜨는데 클라이언트에 response되지 않는 거냐고ㅠㅠ
알고 봤더니 스프링시큐리티가 관여하는(로그인 부분-_-) 부분에서의 실패처리는 또 config에서 설정해주고 AuthenticationFailureHandler를 상속받은 핸들러를 만들어서 처리해줘야 하는 거였다-_-
너무한 거 아니냐고ㅠㅠ 일일이 확인받고 보고받아야 되는 인증관리자님;;




그래서 전까지는 과제 해결하는데 크게 어려움이 없었다가 이번에 제대로 대가리가 깨져버렸다 ㅎㅎㅎ;
진짜 과제 제출 못 할 뻔 ㅎㅎ;
실제로 캠프 내에서 이번 과제를 제출하지 못한 분들도 많았고ㅠㅠ 사실상 나도 javascript로 ㅋㅋㅋ 해결해버려서 참…
이것이… 숙련…?



숙련주차 때 만든 CRUD페이지

DI, IoC, Bean

DI (의존성 주입)


강한 결합의 문제


예를들어 Controller에서 Service의 객체를 생성하여 사용하고 Service에서 Repository의 객체를 생성하여 사용하고 Repository에서 객체를 선언한다면, Repository 생성자가 변경되었을 때 모든 Controller와 Service의 코드 변경이 필요한데 이 문제를 해결하기 위해서

강한 결합 해결방법

  1. 각 객체에 대한 객체 생성은 딱 1번만
  2. 생성된 객체를 모든 곳에서 재사용

해야 한다.

예를들면,

  1. Repository에서 클래스 선언 및 객체 생성을 하고
    public class Repository{...}
    //객체 생성
    Repository repository = new Repository();
    
  2. Service에서 클래스 선언 및 객체 생성(repository 사용)
    Class Sevice{
     private final Repository repository;
     //repository 객체 사용
     public Service(Repository repository){
         this.repository = repository;//new Repository(); 아니고
     }
    }
    //객체 생성
    Service sevice = new Service(repository);
    
  3. Controller 선언(service 사용)
    Class Controller{
     private final Service service;
     //service 객체 사용
     public Controller(Service service){
         this.service = service;//new Service(); 아니고
     }
    }
    

이렇게 하면 만약 Repository에서 생성자가 변경되어도 Service와 Controller가 변경될 필요가 없다.
결론적으로 강한 결합에서 느슨한 결합으로 바뀐 것이다.




IoC (Inversion of Control)


제어의 역전이란 뜻으로 프로그램의 제어 흐름이 뒤바뀐 걸 의미한다.
일반적으로는 사용자가 자신이 필요한 객체를 생성해서 사용했다면 DI(의존성 주입)으로 인해 IoC 즉, 제어의 역전이 일어나면서(예를 들어, 전에는 Repository를 만들고 Service -> Controller를 생성했다면 IoC에 의해 Controller -> Service -> Repository 순이 되었다는 것?)

  • 용도에 맞게 필요한 객체를 가져다 사용
  • 사용할 객체가 어떻게 만들어졌는지 알 필요 없음
  • 실생활 예제) 가위의 용도별 사용
    • 음식을 자를 때 필요한 부엌가위(생성되어 있는 객체 kitchenScissors)
    • 무늬를 내며 자를 때 필요한 핑킹가위(생성되어 있는 객체 pinkingShears)
    • 정원 나무를 다듬을 때 필요한 전지가위(생성되어 있는 객체 pruningShears)



Bean


앞선 DI를 사용하기 위해서는 객체 생서잉 우선되어야 했다. 그리고 그 필요한 객체를 생성하여 관리하는 역할을 대신 해주는 게 바로 스프링 프레임워크다.
그리고 Bean은 스프링이 관리하는 객체고, 스프링 IoC 컨테이너는 빈을 모아둔 통이라고 생각하면 된다.



스프링 빈 등록 방법


  1. @Component 클래스 선언 위에 설정하고
@Component
public class TestService{...}

스프링 서버가 뜰 때 스프링 IoC에 빈 저장

//@Component 클래스에 대해 스프링이 해주는 일

//1. Service 객체 생성
TestService testService = new TestService();
//2. 스프링 IoC 컨테이너에 빈(testService) 저장
// testService -> 스프링 IoC 컨테이너

스프링 빈 이름: 클래스 앞글자만 소문자로 변경 public class TestService -> testService

빈 아이콘 확인 img

@Component 적용 조건

  • @ComponentScan에 설정해준 packages 위치와 하위 packages들
  • @SpringBootAplication에 의해 default로 설정되어 있음
  1. @Bean 직접 객체를 생성하여 빈으로 등록 요청
@Configuration
public class BeanConfiguration{
    @Bean
    public TestRepository testRepository(){...}
}

스프링 서버가 뜰 때 스프링 IoC에 빈 저장

//1. @Bean 설정된 함수 호출
TestRepository testRepository = beanConfiguration.testRepository();
//2. 스프링 IoC 컨테이너에 빈(testRepository) 저장
//testRepository -> 스프링 IoC 컨테이너

스프링 빈 이름: @Bean이 설정된 함수명 public TestRepository testRepository(){…} -> testRepository

빈 아이콘 확인 -> 스프링 IoC에 빈이 등록될 것이라는 표시



스프링 빈 사용방법


  1. @Autowired 멤버변수 선언 위에 @Autowired를 하면 스프링에 의해 DI됨
@Component
public class TestService{
    @Autowired
    private TestRepository testRepository;
}

빈을 사용할 함수 위에 @Autowired를 하면 스프링에 의해 DI됨

@Component
public class TestService{
    private final TestRepository testRepository;
    @Autowired
    public TestService(TestRepository testRepository){
        this.testRepository = testRepository;
    }

}

@Autowired 적용 조건
스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 가능

@Autowired 생략 조건

  • Spring 4.3버전부터 생략 가능
  • 생성자 선언이 1개일 때만 생략 가능
  • Lombok의 @RequiredArgsConstructor를 사용하면
@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class TestController {

    private final TestService testService;
    
    // 생략 가능
		// @Autowired
		// public TestController(TestService testService) {
		//     this.testService = testService;
		// }
}
  1. ApplicationContext
    스프링 IoC 컨테이너에서 빈을 수동으로 가져오는 방법
@Component
public class TestService {
    private final TestRepository testRepository;

    @Autowired
    public TestService(ApplicationContext context) {
        // 1.'빈' 이름으로 가져오기
        TestRepository testRepository = (TestRepository) context.getBean("testRepository");
        // 2.'빈' 클래스 형식으로 가져오기
        // TestRepository testRepository = context.getBean(TestRepository.class);
        this.testRepository = testRepository;
    }	
}

출처: 스파르타코딩클럽 Spring 심화반

객체지향 프로그래밍과 JVM

객체지향 프로그래밍


자바 공부를 하다보면 객체지향 어쩌고 저쩌고 하는 말을 자주 듣게 되는데, 도대체 객체지향이 무엇인가.



개념


일단 객체지향 프로그래밍이란 컴퓨터 프로그래밍의 패러다임 중 하나다.
컴퓨터 프로그램을 여러 개의 독립된 객체(object)들의 모임으로 보는 것이다.

뭐 다른 건 다 됐고, 그래서 왜 객체지향적인 프로그래밍을 해야 하지?

지금이야 공부를 시작하는 단계고, 아직 혼자서 구글링에 의지하며 꾸역꾸역 코딩을 하고 있지만.
기본적으로 개발은 다같이 하게 된다. 다른 개발자가 쓴 코드를 내가 읽고 수정해야 할 수도 있고.
그런데 그 코드가 꼬이고 꼬여서 함수가 어떻게 작동을 하는 건지 영향 받는 변수를 싹 다 조사해봐야 한다면?

이를 위한 게 객체지향 프로그래밍이다. 큰 문제를 작게 쪼개는 것이 아니라, 작은 문제들을 해결할 수 있는 객체들을 만들고 그 객체들을 조합해서 큰 문제를 해결하는 방식, 상향식 해결법(Bottom-Up).

객체가 독립성을 가지고 신뢰성이 높게 만들어 놓으면 그 객체를 수정 없이 재사용 할 수 있으니까 개발이 편해진다.

하지만 요즘 프로그램은 워낙 복잡해져서… 뭐 단순히 객체지향 프로그래밍을 하면 코드가 간결해진다거나 알아보기 쉽다거나 유지보수가 편하다거나 하는 말은 어렵게 됐지만 ㅋㅋ
그 때문에 이걸 간결하게 정리하려고 ‘디자인 패턴’이란 것도 생겼다고 한다. 협업을 전제로 한 환경에서는 특히 중요한 프로그래밍 형식을 정하는 약속이다.
물론 지금은 봐도 이게 뭔 소린지 ㅋㅋ 하지만 개발자로 취직을 하기 위해선 반드시 알아둬야겠다.



결론은


뭐 말이 길었지만 결론적으로는 간결하고 아름다운 코딩을 하자! 인… 걸까?ㅋㅋ

사실 객체지향적 프로그래밍에도 단점은 보이는데, 유지보수에 대해 말하자면 대게 ‘상속’이 복잡하게 얽혀있으면ㅋㅋ
캡슐화 자체도 객체의 독립성면에서 객체 지향 프로그래밍에서 빠질 수 없는 요소긴 하지만, 함수 호출에 시간이 든다거나? 그래서 절차적 프로그래밍보다는 무거워진다고 하더라.

또, 객체 하나하나를 따로 나누는데 주력하다보니 서로 비슷한 처리를 하는 코드가 서로를 건드릴 수 없게 되었고 이를 해결하기 위해 getter(접근자: 값을 반환하는 메소드), setter(설정자: 필드에 값을 저장하는 메소드)사용이 너무 많아졌다. 이 과정에서 캡슐화가 깨지고 그냥 public으로 공개한 경우나 마찬가지인 상태가 되어 의미가 퇴색되었고, 다른 프로그래밍 패러다임이 필요해졌다. AOP(Aspect Oriented Programming)는 모든 코드 하나하나를 별개의 객체로 분리하기보다 ‘어떤 일을 어디서 처리하는가’ 에 더 중점을 두어 큰 범위로 묶어주어 모듈화 효율을 개선시켰고, 현대 프로그래밍 언어와 코드들은 원본 객체 지향 방식을 그냥 그대로 적용하는 경우가 드물다고 한다. 출처 나무위키 ㅋㅋ




JVM


JVM은 말 그대로 Java자바 Virtual가상 Machine머신으로(아 뭐야 가상만 한글이넼ㅋㅋ) OS에 종속받지 않고 CPU가 Java를 인식, 실행할 수 있게 하는 가상 컴퓨터라고 한다.

쉽게 말하면, 우리가 java파일에 쓴 코드 그 자체는 CPU가 인식을 하지 못하니까 JVM이라는 가상머신을 거쳐서 인식할 수 있게 class파일로 컴파일하는 것이다.

다들 JDK 설치하고 cmd에 println(“Hello Java”);(혹은 월드)하고 javac로 컴파일 해보지 않으셨는지?ㅋㅋ