Integer, Long, Double : 래퍼타입 → 기본형 데이터 타입을 객체로 만든 것
Class : 클래스 메타 정보
System : 시스템과 관련된 기본 기능들 제공
Object 클래스
자바에서 모든 클래스의 최상위 부모 클래스는 항상 Object 클래스 이다.
package lang.object;
// 부모가 없으면 묵시적으로 Object 클래스를 상속 받는다.
public class Parent {
public void parentMethod() {
System.out.println("Parent.parentMethod");
}
}
package lang.object;
// Parent 를 명시적으로 상속 받늗다.
public class Child extends Parent{
public void childMethod() {
System.out.println("Child.childMethod");
}
}
⇒ 다음과 같은 상속 구조를 갖게 된다.
⇒ 모든 자바의 최종 부모는 Object 가 되게 된다.
package lang.object;
public class ObjectMain {
public static void main(String[] args) {
Child child = new Child();
child.childMethod();
child.parentMethod();
// toString 은 Object 클래스의 메서드 => Object 로 부터 상속 받아 쓰게 된다.
String string = child.toString();
System.out.println(string);
}
}
⇒ Parent 는 Objcet 를 묵시적으로 상속 받았기 때문에 child 클래스 내에서도 Object 의 기능인 toString 을 사용 할 수 있는것 이다.
자바에서 Object 클래스가 최상위 부모 클래스인 이유??
공통 기능 구현
모든 객체에서 필요한 공통 기본 기능을 개발자가 직접 메서드를 만드는 것은 상당히 번거롭고 개발자 마다 메서드의 기능, 명 이 일관 되지 않기 때문에 공통 기능 구현 및 통일성을 위해 최상위 클래스로 사용되게 된다. → 프로그래밍의 단순화, 일관성의 목적
Object 제공하는 기능의 예
객체 정보 제공 → toString()
객체의 같음 을 비교 → equals() 등등의 기능이 있다.
다형성의 기본 구현
Object 는 모든 class 들의 부모 class 타입 이다. → 따라서 어떤 타입으로 값을 받아와도 Object 타입으로 받아 올 수 있다.
Object 다형성
Object 는 모든 클래스의 부모 클래스 이므로 → 모든 객체를 참조 할 수 있다 .
Object dog = new Dog();
Object car = new Car();
⇒ Object 는 모든 객체의 부모 타입이므로 어떤 객체든 담을 수 있다.
Object 타입의 Car class
package lang.object.poly;
public class Car {
public void move() {
System.out.println("자동차 이동");
}
}
Object 타입의 Dog class
package lang.object.poly;
public class Dog {
public void sound() {
System.out.println("멍멍");
}
}
Car 타입과 Dog 타입 class 를 사용 하는 클래스에서 Object 타입의 메서드 사용
package lang.object.poly;
public class ObjectPolyExample1 {
public static void main(String[] args) {
Dog dog = new Dog();
Car car = new Car();
action(dog);
action(car);
}
private static void action(Object obj) {
// obj.sound(); // 컴파일 오류 Object 는 sound() 기능이 없다.
// obj.move(); // 컴파일 오류 , Object sms move() 가 없다.
// 객체에 맞는 다운캐스팅 으로 호출 가능
if (obj instanceof Dog dog) {
dog.sound();
} else if (obj instanceof Car car) {
car.move();
}
}
}
⇒ 결론 Object 는 자식을 모두 담을 수 있지만 자식 내에 무었이 들어 있는지 자세히 알 순 없다.
⇒ 그래서 다운 캐스팅을 사용해 준다.
Object 배열
Object 는 모든 타입의 객체를 담을수 있고 Object 타입의 배열을 만들면 세상의 모든 객체를 담을 수 있는 배열이 된다.
Object - toString() 메서드
toString 메서드는 객체에 저장된 정보를 문자열 형태로 반환 해주는 기능을 말한다. → 디버깅과 로깅때 유용하게 사용 된다.
toString 오버라이딩
기본적으로 toString 메서드를 사용해 나온 객체 값은 사용하기 쉽지 않아 오버라이딩 하여 사용한다.
메서드 오버라이딩하여 toString을 사용할 수 있다 Dog class 만 오버라이딩 하여 사용해보면
package lang.object.tostring;
public class Dog {
private String dogName;
private int age;
public Dog(String dogName, int age) {
this.dogName = dogName;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"dogName='" + dogName + '\\'' +
", age=" + age +
'}';
}
}
⇒ Dog class
package lang.object.tostring;
public class Car {
private String carName;
public Car(String carName) {
this.carName = carName;
}
}
⇒ Car class
package lang.object.tostring;
public class ToStringManin2 {
public static void main(String[] args) {
Car car = new Car("Model Y");
Dog dog1 = new Dog("멍멍이1", 2);
Dog dog2 = new Dog("멍멍이2", 5);
System.out.println("1. 단순 toString 호출");
System.out.println(car.toString());
System.out.println(dog1.toString());
System.out.println(dog2.toString());
System.out.println("2. println 내부에서 toString 호출");
System.out.println(car);
System.out.println(dog1);
System.out.println(dog2);
}
}
⇒ Dog 와 Car 를 사용하면
1. 단순 toString 호출
lang.object.tostring.Car@7a81197d
Dog{dogName='멍멍이1', age=2}
Dog{dogName='멍멍이2', age=5}
2. println 내부에서 toString 호출
lang.object.tostring.Car@7a81197d
Dog{dogName='멍멍이1', age=2}
Dog{dogName='멍멍이2', age=5}
⇒ 메서드 오버라이딩 한것은 한것대로 결과가 나오고 println에서도 오버라이딩 한것을 호출 한다.
public class ObjectPrinter {
public static void print(Object obj) {
String string = "객체 정보 출력: " + obj.toString();
System.out.println(string);
}
}
⇒ ObjectPrinter 라는 class 는 Object class에 의존 한다고 표현 하는데 Object class 는 구체적인 Dog나 Car 같은 우리가 생성한 구체적 class 를 의존하는것이 아니기 때문에 추상적인것을 의존한다 라고 표현한다.
ObjectPrinter class의 특징 살펴보기
다형적 참조 - print(Object obj) 타입의 매개변수를 사용하여 다형적 참조를 가능하게 한다 → Dog, Car 객체등 모든 객체 받을 수 있게
메서드 오버라이딩 - Object 클래스는 모든 객체의 부모 이기때문에 Object가 갖고 있는 모든 메서드 (toString()) 과 같은 것을 모든 클래스에서 메서드 오버라이딩 할 수 있다.
OCP 원칙
Open : 새로운 클래스를 추가, toString() 을 오버라이딩해 기능 확장 가능
Closed : 새로운 클래스가 추가되도 ObjectPrinter는 변경하지 않고 그대로 사용 가능하다.
정리
지금까지 배운 내용은 System.out.println() 의 기능을 살펴본 것이다.
자바 언어가 기본 제공하는 다양한 메서드들은 개발자가 필요에 따라 오버라이딩 해서 사용할 수 있도록 설계되어 있다.
의존관계?
정적 의존관계 : 주로 class 간의 관계를 의미하고, class 관계 그림이 정적 의존 관계이다, 프로그램을 실행시키지 않고 class 내에 사용하는 타입들만 보고 쉽게 의존관계를 파악할 수 있다.
동적 의존관계 : 프로그램을 실행하는 런타임 에서 확인할 수 있는 관계, Object 타입으로 모든 것을 받는 경우 프로그램을 실행 시켜 Car 타입인지 Dog 타입인지 알 수 있는 관계를 말한다.
⇒ 단순히 의존관계 또는 어디에 의존한다고 하면 주로 정적 의존 관계를 말한다.
equals() - 1. 동일성과 동등성
동등성 : Object 에서 제공하는 동등성 비교를 위한 equals() 메서드 이다. → 두 객체가 논리적으로 동등한지 확인
동일성 : == 연산자를 사용하여 두 객체의 참조가 동일한 객체를 가리키는지 확인 → 완전히 같은지 확인
equals 실행 순서 예시
user1.equals(user2)
return (user1 == user2) // Object.equals 메서드 안
return (x001 == x002) // Object.equals 메서드 안
return false
false
⇒ equals 메서드는 위와 같이 작동한다. 그러므로 특정한 값의 동등성 비교를 사용하고 싶으면 equals 메서드를 재정의 해야 한다
equals() - 2. 구현
package lang.object.equals;
public class UserV2 {
private String id;
public UserV2(String id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
// 인자로 넘어온 id 를 변수에 담기 Object 에는 id가 없어서 다운 캐스팅
UserV2 user = (UserV2) obj;
// 내 id와 넘어온 id 같은지 비교 하기
return id.equals(user.id);
}
}
⇒ equals 메서드를 오버라이딩 하여 비교할 변수 를 지정해 변수끼리 비교 하게 만들기
package lang.object.equals;
public class EqualsMainV2 {
public static void main(String[] args) {
UserV2 user1 = new UserV2("id-100");
UserV2 user2 = new UserV2("id-100");
System.out.println("identity = " + (user1 == user2));
System.out.println("equality = " + user1.equals(user2));
}
}
권한 A, B, C 가 있고 이중 권한이 A < B < C 로 진행 하고 싶은경우 계층권한을 사용하면 된다.
(사용방법 으로는 SecurityConfig 파일 내부에 🔽)
// 계층 권한 설정
@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_C > ROLE_B > ROLE_A"); // 권한 설정 순위 셋팅
return hierarchy;
}
⇒ 해당 코드를 추가 해주고
// 이제 내부에 어떤 경로에 요청이 왔을때에 어떤 응답을 해줄지 설정 해주면 된다.
// 작성방식은 람다식으로 해주면 된다.
http.authorizeHttpRequests((auth) -> auth
.requestMatchers("/","/login").permitAll()
.requestMatchers("/my/**").hasAnyRole("A") // <- A,B,C 모두 들어감
.requestMatchers("/admin").hasAnyRole("C") // <- C만 들어갈 수 있음
.anyRequest().authenticated()
);
CSRF (Cross Site Request Forgery) 로 요청을 위조하여 사용자가 원하지 않아도 서버측으로 특정 요청을 강제로 보내는 방식
개발 환경에서 Security Config 클래스를 통해 csrf 설정을 disable 설정을 했지만 실제 서비스 환경에서는 csrf 공격을 방지하기위해 csrf disable 설정을 제거하고 추가적인 설정을 진행해야 한다.
(기존 개발 환경에서 🔽)
// 로그인을 하게 되면 csrf 라는 토큰이 필요한데 지금 과정에서는 disable 상태로 두고 개발함
http.csrf((auth) -> auth.disable());
(배포할 시에 수정 코드 🔽)
// 로그인을 하게 되면 csrf 라는 토큰이 필요한데 지금 과정에서는 disable 상태로 두고 개발함
// http.csrf((auth) -> auth.disable());
이렇게 csrf 코드를 주석 처리 해주면 로그인 기능을 사용 할 수 없게 된다.
해당 코드를 주석 처리 하게 되면 enable 처리가 된어 csrf 설정이 진행된다.
enable 처리가 되면 스프링 시큐리티는 CsrfFilter를 통해
POST, PUT, DELETE 요청에 대해 토큰 검증을 진행한다.
(그래서 csrf 토큰을 관리하는 시스템을 구축하여야 한다. 🔽)
(mustache- post 요청에서 설정 기준)
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Login Page</title>
</head>
<body>
login page
<hr>
<form action="/loginProc" method="post" name="loginForm">
<input id="username" type="text" name="username" placeholder="id"/>
<input id="password" type="password" name="password" placeholder="password"/>
<input type="hidden" name="_csrf" value="{{_csrf.token}}"/>
<!-- 위의 코드를 집어 넣어 post 요청 코드에 같이 보낸다. -->
<input type="submit" value="login"/>
</form>
</body>
</html>
사용자가 로그인을 하게 되면 사용자의 정보는 SecurityContextHolder에 의해 서버 세션에 관리된다. → 이번 시간에는 세션의 소멸 시간, 아이디당 세션 생성 개수 설정하는것을 배울 예정이다.
세션 소멸 시간 설정
세션 타임 아웃 설정 → 특정 요청을 수행한 뒤 설정 시간 만큼만 유지
(application.properties 파일에서 🔽)
server.servlet.session.timeout=3000
⇒ 위와 같이 작성하면 3000 초간 로그인이 유지된다.
다중 로그인 설정
동일한 아이디로 다중 로그인을 진행할 경우 설정 방법
공식 문서의 sesstion Management 를 통해 구현 방법이 나와있다.
(SecurityConfig.java 파일에서 🔽)
// 다중 로그인 설정
http.sessionManagement((auth) -> auth
.maximumSessions(1) // <- 하나의 아이디에 동시접속 수
.maxSessionsPreventsLogin(true) // <- true: 해당 값을 초과할 경우 새 로그인 차단
// false: 기존 로그인 아웃 새로그인 진행
);
세션 고정 보호 (해커의 admin 세션 탈취를 보호)
sesstionManagement((auth) → auth.sessionFixation().none()); : 로그인 시 세션 정보 변경 안함
sesstionManagement((auth) → auth.sessionFixation().newSession()); : 로그인 시 세션 새로 생성
sesstionManagement((auth) → auth.sessionFixation().changeSessionId()); : 로그인 시 동일한 세션에 대한 id 변경 ⇒ 주로 이걸 사용하여 보호한다.
// 세션 고정 보호
http.sessionManagement((auth) -> auth.sessionFixation().changeSessionId());