1. 230515
1) JPA
-
AJAX로 모든 프론트단을 설계하고 도배질하려면, JPA로 구현하면 된다. 쿼리가 길어진다면, 무조건 View 테이블로 만들어서 Join도 최대한 줄이고 동적쿼리도 줄일것
-
하지만, JOIN을 많이 하려면, Mybatis를 이용해야 한다.
-
nativeQuery로 설정을 해주면, Mybatis나 MariaDB로 변환되는 것인데 JPA를 쓰는 것은 nativeQuery를 안 된다.
2) 구글 로그인 아이콘 변경하는 방법 :
-
현재, 기존 Google 로그인 버튼의 코드에서 아이콘을 추가하면, 에러가 발생한다.
-
우리는 사용자가 만든 custom 버튼으로 설정 해야 한다.
-
즉, 구글 로그인할 때, auth(코드 기반)나 token(토큰 기반)인지 판단해야한다.
-
구글 로그인에서 ‘popup-type’이나 ‘prompt’ 설정이 필요하다.(옵션)
- ‘popup-type’은 토큰으로 쓸지 코드 기반으로 쓸지 설정해준다.
3) 구글 로그인 아이콘 변경하는 방법 실습 :
a. 구글 로그인 실습 코드
- popup-type 중요!!
- 구글 로그인에서 ‘popup-type’이나 ‘prompt’ 설정이 필요하다.(옵션)
- ‘popup-type’은 토큰으로 쓸지 코드 기반으로 쓸지 설정해준다.
function customLoginHandler(response){
// console.log("hihihi");
fetch(`https://www.googleapis.com/oauth2/v3/userinfo?access_token=${response.access_token}`)
.then(res=>res.json())
.then(credential=>{
console.log(credential);
// **전역 객체(global object)에 인증 정보를 담기**
// userDetails.username = userData.name;
// userDetails.email = userData.email;
// userDetails.roles = ["ADMIn","MEMBER"];
router.push("/signupWithGoogle");
})
.catch(e=>{
console.log("error");
});
}
<div class="wd-100">
<GoogleLogin :callback="customLoginHandler" class="wd-100" popup-type="TOKEN">
<span class="deco icon-logo-google btn btn-outline">구글로 로그인</span>
</GoogleLogin>
</div>
- 구글 로그인 전체 실습 코드
<script setup>
import { reactive } from 'vue';
import { useRouter, useRoute } from 'vue-router';
// 전역변수가 필요한데 어떻게 하지? =>모듈만들어서 붙인다
// import userDetails from '../stores/UserDeatails.js';
import { useUserDetailsStore } from '../stores/useUserDetailsStore';
import { decodeCredential } from 'vue3-google-login'
let userDetails = useUserDetailsStore();
let router = useRouter();
let route = useRoute();
let user = reactive({
username:"",
password:"",
roles:"",
})
// async, await로 따로 fetch 하지 않고 한 번에 fetch 하자!
// 예전에는 상태를 유지하는 stateful 방식이었다.
// 하지만, 요즘에는 상태를 유지하지 않고 stateless 방식이다.
function customLoginHandler(response){
// console.log("hihihi");
fetch(`https://www.googleapis.com/oauth2/v3/userinfo?access_token=${response.access_token}`)
.then(res=>res.json())
.then(credential=>{
console.log(credential);
// **전역 객체(global object)에 인증 정보를 담기**
// userDetails.username = userData.name;
// userDetails.email = userData.email;
// userDetails.roles = ["ADMIn","MEMBER"];
router.push("/signupWithGoogle");
})
.catch(e=>{
console.log("error");
});
}
function googleLoginHandler(response){
let userData = decodeCredential(response.credential);
console.log(userData);
userDetails.username = userData.name;
userDetails.email = userData.email;
userDetails.roles=["ADMIN","MEMBER"];
let returnURL = route.query.returnURL;
if(returnURL)
router.push(returnURL);
else
router.push("/index");
}
async function loginHandler(){
console.log(user.username)
let response = await fetch("http://localhost:8080/members/login",{
method:"POST",
headers:{
"Accept": "application/json",
"Content-type":"application/x-www-form-urlencoded"
},
body:`username=${user.username}&password=${user.password}`
});
let json = await response.json();
console.log(json)
//console.log(json.result.email);
//전역 객체(global object)에 인증 정보를 담기
userDetails.username = json.result.userName;
userDetails.email = json.result.email;
userDetails.roles=json.roles;
let returnURL = route.query.returnURL;
if(returnURL)
router.push(returnURL);
else
router.push("/index");
}
</script>
<template>
<main>
<div class="sign-in">
<div class="sign-in-logo">
<img src="/image/logo-black.svg" alt="Rland" />
</div>
<form class="sign-in-form">
<div class="sign-in-form-input">
<div>
<input
type="text"
class="input-bottom-line"
placeholder="아이디"
required
v-model="user.username"
/>
</div>
<div>
<input
type="password"
class="input-bottom-line"
placeholder="비밀번호"
required
v-model="user.password"
/>
</div>
</div>
<div class="sign-in-form-button" >
<div class="wd-100">
<input type="submit" value="로그인" class="btn btn-default" @click.prevent="loginHandler" />
</div>
<div class="font-14">또는</div>
<div class="wd-100">
<!-- <a href="" class="deco icon-logo-google btn btn-outline"
>구글로 로그인</a
> -->
<GoogleLogin :callback="customLoginHandler" class="wd-100" popup-type="TOKEN">
<span class="deco icon-logo-google btn btn-outline">구글로 로그인</span>
</GoogleLogin>
</div>
<div>
<GoogleLogin :callback="googleLoginHandler" />
</div>
</div>
</form>
<div class="sign-in-find-user font-14">
<a href="./sign-up.html">회원가입</a> |
<a href="">비밀번호 찾기</a>
</div>
</div>
</main>
</template>
<style scoped>
@import url("/css/sign-in.css");
main{
}
</style>
4. 백단에서 구글 로그인
a. 라이브리 추가
-
타임리프 라이브러리 추가
-
OAuth2 Client 라이브러리 추가
b. 기존의 DefaultService(DB 썼던 것) 대신에 구글 사용자 정보로 추가해주기
- SecurityConfig로부터 사용자 DB용 서비스를 구글 로그인용 서비스로 변경해주기
package kr.co.rland.rlandapiboot3.auth;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
@Service
public class GoogleUserDetailService extends DefaultOAuth2UserService{
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// 유저 정보를 바로 반환하지 말고 잠시 담았다가 반환해주기!
OAuth2User user = super.loadUser(userRequest)
System.out.println(user.getAttribute());
return user;
}
}
c. application.properties에 사용자 정보 간단히 추가
logging.level.org.hibernat.SQL=DEBUG
spring.jpa.properties.hibernate.format_sql=true
spring.security.oauth2.client.registration.google.client-id=
spring.security.oauth2.client.registration.google.client-secret=
d. 타임리프에서 템플릿 설정 추가(기본 템플릿 구현)
- index.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>로그인 페이지</h1>
<div>
<a href="">구글 로그인</a>
</div>
</body>
</html>
- user/login.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Login Page</h1>
</body>
</html>
- member/index.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>로그인 페이지</h1>
<div>
<a href="">구글 로그인</a>
</div>
</body>
</html>
- HomeController.java
@Controller
@RequestMapping("/")
public class HomeController {
@GetMapping("index")
public String index(){
return "index";
}
}
- MemberController.java
@Controller
@RequestMapping("member")
public class MemberController {
// @Autowired
// private MemberService service;
@GetMapping("index")
public String index(){
return "member/index";
}
}
- UserController.java
@Controller
@RequestMapping("user")
public class UserController {
@GetMapping("login")
public String login(){
return "user/login";
}
}
e. SpringSecurity Config에서 Oauth2 추가 설정
a) console.cloud.google.com에서 SpringBoot용 Authorized Redirect URIs
설정 추가
http://localhost:8080/login/oauth2/code/google
b) .oauth2Login();
를 추가로 설정!
package kr.co.rland.rlandapiboot3.auth;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class RlandSecurityConfig {
// 인증 '필터' 추가하기!!(수문장)
// 1. 내가 원하는 URL만 보안 설정하자!
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
// 2. 역할자에 따라서 권한을 부여해라!
// 1) .csrf().disable() : cors 설정도 풀어줘야 한다 .
http
.csrf().disable()
.authorizeHttpRequests(authorize->authorize
.requestMatchers("/admin/**").hasAnyRole("ADMIN")
.requestMatchers("/member/**").hasAnyRole("ADMIN","MEMBER")
.anyRequest().permitAll())
.oauth2Login();
return http.build();
}
// 위에 설정까지만 하면 회원이 있는지 설정해줘야 한다!
// @Bean
// public UserDetailsService userDetailsService(){
// return new RlandUserDetailService();
// }
}
e. 타임리프 템플릿 추가 설정!
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>로그인 페이지</h1>
<div>
<a href="/oauth2/authorization/google">구글 로그인</a>
<!-- 처음에는 access 에러가 발생 -->
</div>
</body>
</html>
5. 로그아웃 하기
a. Config 추가 설정
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/index");
a) 실습 코드
- SecurityConfig.java
package kr.co.rland.rlandapiboot3.auth;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class RlandSecurityConfig {
// 인증 '필터' 추가하기!!(수문장)
// 1. 내가 원하는 URL만 보안 설정하자!
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
// 2. 역할자에 따라서 권한을 부여해라!
// 1) .csrf().disable() : cors 설정도 풀어줘야 한다 .
http
.csrf().disable()
.authorizeHttpRequests(authorize->authorize
.requestMatchers("/admin/**").hasAnyRole("ADMIN")
.requestMatchers("/member/**").hasAnyRole("ADMIN","MEMBER")
.anyRequest().permitAll())
.oauth2Login()
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/index");
return http.build();
}
// 위에 설정까지만 하면 회원이 있는지 설정해줘야 한다!
// @Bean
// public UserDetailsService userDetailsService(){
// return new RlandUserDetailService();
// }
}
- index.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>로그인 페이지</h1>
<div>
<!-- 처음에는 access 에러가 발생 -->
<a href="/oauth2/authorization/google">구글 로그인</a>
<a href="/user/logout">로그아웃</a>
</div>
</body>
</html>
- user.login.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Login Page</h1>
<div>
<!-- 처음에는 access 에러가 발생 -->
<a href="/oauth2/authorization/google">구글 로그인</a>
<a href="/user/logout">로그아웃</a>
</div>
</body>
</html>
6) 사용자가 만든 폼으로 구글 로그인 설정 1
.formLogin() // 사용자가 만든 폼 형태로 로그인하기!
.loginPage("/user/login") // GET (사용자에게 문서를 준다.)
.loginProcessingUrl("/user/login") // 사용자가 POST 요청!!
.defaultSuccessUrl("/index") // 로그인 성공시 이동하는 URL!!
.and()
a. 폼 관련 실습 코드 :
package kr.co.rland.rlandapiboot3.auth;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class RlandSecurityConfig {
// 인증 '필터' 추가하기!!(수문장)
// 1. 내가 원하는 URL만 보안 설정하자!
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
// 2. 역할자에 따라서 권한을 부여해라!
// 1) .csrf().disable() : cors 설정도 풀어줘야 한다 .
http
.csrf().disable()
.authorizeHttpRequests(authorize->authorize
.requestMatchers("/admin/**").hasAnyRole("ADMIN")
.requestMatchers("/member/**").hasAnyRole("ADMIN","MEMBER")
.anyRequest().permitAll())
.formLogin() // 사용자가 만든 폼 형태로 로그인하기!
.loginPage("/user/login") // GET (사용자에게 문서를 준다.)
.loginProcessingUrl("/user/login") // 사용자가 POST 요청!!
.defaultSuccessUrl("/index") // 로그인 성공시 이동하는 URL!!
.and()
.oauth2Login()
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/index");
return http.build();
}
// 위에 설정까지만 하면 회원이 있는지 설정해줘야 한다!
// @Bean
// public UserDetailsService userDetailsService(){
// return new RlandUserDetailService();
// }
}
7) 사용자가 만든 폼으로 구글 로그인 설정 2
-
구글 로그인에는 아까 이전에 auth(코드 기반)나 token(토큰 기반)기반 처럼 openId 형식, Autherized 형식이 있다.
-
userInfoEndpoint가 사용자 정보를 전달해주면, userService가 사용자 정보를 받는다!
.oauth2Login()
.userInfoEndpoint() // userInfoEndpoint가 사용자 정보를 전달해주면,
.userService(googleUserDetailService); // userService가 사용자 정보를 받는다!
a. 실습 코드
package kr.co.rland.rlandapiboot3.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class RlandSecurityConfig {
@Autowired
private GoogleUserDetailService googleUserDetailService;
// 인증 '필터' 추가하기!!(수문장)
// 1. 내가 원하는 URL만 보안 설정하자!
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
// 2. 역할자에 따라서 권한을 부여해라!
// 1) .csrf().disable() : cors 설정도 풀어줘야 한다 .
http
.csrf().disable()
.authorizeHttpRequests(authorize->authorize
.requestMatchers("/admin/**").hasAnyRole("ADMIN")
.requestMatchers("/member/**").hasAnyRole("ADMIN","MEMBER")
.anyRequest().permitAll())
.formLogin() // 사용자가 만든 폼 형태로 로그인하기!
.loginPage("/user/login") // GET (사용자에게 문서를 준다.)
.loginProcessingUrl("/user/login") // 사용자가 POST 요청!!
.defaultSuccessUrl("/index") // 로그인 성공시 이동하는 URL!!
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/index")
.and()
.oauth2Login()
.userInfoEndpoint() // userInfoEndpoint가 사용자 정보를 전달해주면,
.userService(googleUserDetailService); // userService가 사용자 정보를 받는다!
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// 위에 설정까지만 하면 회원이 있는지 설정해줘야 한다!
// @Bean
// public UserDetailsService userDetailsService(){
// return new RlandUserDetailService();
// }
}
3. JDK 8, 리눅스 시작 : 230517
1) JAVA 8
-
lambda, functional interface, stream API, optional class 등등
-
이것은 ‘콜렉션의 Data 분석 기능’을 위해서 존재하는 기능들이다.
-
Arrays : 정적인 변수이다.
5) 전체 코드 :
- Program.java
package main.java.ex1.sort;
import java.util.Arrays;
public class Program{
public static void main(String[] args){
// ['객체 정렬']
// ********* 1. 숫자형 배열 정렬 ***************
// compare는 '객체 정렬'이라서 int 타입이 아니라 Integer 타입으로 사용할 것!
Integer[] ages = {10,30,3,34,53,23,40};
// Arrays.sort(ages); // 오름차순 정렬
// 역정렬은 Compare 연산을 이용해야해서 원래 어렵다. 이를 쉽게 해주는 것이 람다 표현식이다.
Arrays.sort(ages,(a,b)->a-b); // 기본(a-b) : 오름차순 정렬
// b-a : 내림차순 정렬(음수가 결과니까 자리를 바꿔서 내림차순이 된다.)
System.out.println(Arrays.toString(ages));
// ********* 2. 문자열 배열 정렬 ***************
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
// Arrays.sort(stringArray,(a,b)->a-b);
// 문자열은 비교를 할 수 없어서 문자열을 비교하기 위해서 compareTo를 이용하자!*****
Arrays.sort(stringArray, (o1, o2) -> o1.compareTo((o2))); // 오름차순
Arrays.sort(stringArray, (o1, o2) -> o2.compareTo((o1))); // 내림차순
// ********* 3. Method Reference ***************
// 이를 더 간단히 해주기 위해서 compareTo 메서드만 사용하자!
// 하지만, 어디서 선언되었는지 몰라서 이것은 'String::'로 타입을 선언해주자!
// 이러한 것을 'Method Reference'라고 부른다. 하지만, 이것은 오름차순인 기본형태 정렬만 가능하다!
// Arrays.sort(stringArray, (o1, o2) -> o2.compareTo((o1))); // 내림차순
Arrays.sort(stringArray, String::compareTo); // 기본 : 오름차순
System.out.println(Arrays.toString(ages));
// ***** 이러한 모든 것은 Stream API를 위해서 등장했다.
// ********* 4. 인터페이스 개념 ***************
// ** 인터페이스 개념 : 1. 접점이 인터페이스이다. ex) 인터넷 : 네트워크와 네트워크 이어주기,
// 2. Java에서 부품(모듈을)을 갈아끼는 형태이다. 기능을 바꿔낀다. 즉, 다양한 성질을 갖게 된다. 이것을 '다형성'이라고 부른다.
// 3. Java에서 코드 상에서 인터페이스는 ~~이다.
// ******** 5. '람다 표현식'의 등장 배경 *************
// ** 위의 개념을 추가로 데이터 분석에서 많이 사용된다.(파이썬 같은 곳)
// 즉, 람다 표현식은 수식(데이터 분석에서)에서 많이 사용된다. 람다 표현식은 함수 1개만 적용된 것에서만 사용된다.
// 그래서, 메서드가 여러개 인경우 쓸 수가 없던 것이었다.****
// interface Comparable<T>는 저기서 구현할 수 없어서 여기서 익명 클래스로 구현된다!!
// new Comparable<Integer>() {
// @Override
// public int compareTo(Integer o) {
// return 0;
// }
// };
// **** 원래 형태!!
// Comparable<Integer> comp1 = new Comparable<T>() {
// @Override
// public int compareTo(Integer o) {
// return 0;
// }
// };
// 내가 푼 것 : 'Integer o' 인자가 필요하다!
Comparable<Integer> comp0 = () -> {
return 0;
};
// 매개변수와 몸통만 있으면된다.!(함수명만 지우면 된다.)
Comparable<Integer> comp1 = (Integer o) -> {
return 0;
};
// 훨씬더 간단한 형태!
// 매개변수와 몸통만 있으면된다.!
Comparable<Integer> comp2 = o -> 0;
// **************** 6. Object ****************
}
}
- Comparable.java
package main.java.ex1.sort;
@FunctionalInterface
public interface Comparable<T> {
int compareTo(T o);
// 하지만 함수를 하나 더 추가하면, 람다 표현식을 사용할 수 없다!!
// int test();
// *** [Funtional Interface] 개념 ***
// 자바에서는 함수를 전달하는 기능이 이전에는 없었다.
// (무조건 자바에서는 클래스나 인터페이스가 필요하다!)
// @FunctionalInterface 선언하면 된다. 람다식은 여기서만 사용된다!
// [함수형 인터페이스 추가 내용]
// 인터페이스는 함수형 인터페이스입니다.
// default method, static method 를 넣어도 문제 없습니다.
// 어차피 함수형 인터페이스 형식에 맞지 않는다면 @FunctionalInterface 이 다음 에러를 띄워줍니다.
}
6) 리눅스
a. OS 개념, 소프트웨어 개념
-
OS는 실행환경이고 우리는 OS 위에 올려져 있는 소프트웨어를 사용한다.
-
이러한 소프트웨어인 톰캣이나 mariaDB는 리눅스에서 개발된다.
b. MacOS, Windows, Linux 관계
-
Mac은 서버용 OS가 아니다.
-
MacOS는 기반이 UNIX이다. 그래픽만 이용한 것이 MacOs이다.
-
UNIX는 보통 console만 사용된다.
-
UNIX는 에뮬레이터가 필요 없이 톰캣, Oracle, Java JDK 등을 사용할 수 있다.
-
Windows에서는 서버를 돌릴 수 없다. 리소스가 계속 증가해서 재부팅이 필요하다. 하지만, 서버는 안정적인 것이 필요해서 Windows를 사용할 수 없다.
- 리눅스는 안정적이고 여러 사람들에 의해 만들어진 것이며 무료이다.
c. Linux 설치하기
-
보통, 요즘에는 Debian 계열의 Ubuntu 사용!
-
virtual box 이용하기!!
4. Stream API, 리눅스 설치 : 230518
1) Stream API 등장 배경 : JDK 8 미만
-
기본적으로 객체 정렬을 다음과 같이 진행된다.
-
Comparable로부터 implements를 해서 구현해줘야 한다.
a. 실습 코드
- Program.java
package main.java.ex1.sort;
import java.util.Arrays;
public class Program{
public static void main(String[] args){
Exam[] list = {
new Exam(1,2,3),
new Exam(2,3,4),
new Exam(3,2,1)
};
Arrays.sort(list);
System.out.println(Arrays.toString(list));
}
}
- Exam.java
package main.java.ex1.sort;
public class Exam implements Comparable<Exam> {
private int kor;
private int eng;
private int math;
public Exam() {
}
public Exam(int kor, int eng, int math) {
super();
this.kor = kor;
this.eng = eng;
this.math = math;
}
public int getKor() {
return kor;
}
public void setKor(int kor) {
this.kor = kor;
}
public int getEng() {
return eng;
}
public void setEng(int eng) {
this.eng = eng;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int total() {
return kor+eng+math;
}
@Override
public String toString() {
return "Exam [kor=" + kor + ", eng=" + eng + ", math=" + math + "]";
}
@Override
public int compareTo(Exam exam){
return this.total() - exam.total();
}
}
2) Stream API 개념
-
하지만, JDK 8이상의 환경에서는 객체의 정렬을 1)처럼 사용하지 말고 Stream API를 사용한다.
-
엘리먼트(배열이나 콜렉션)을 스트림에다가 functional style(함수의 모습으로)로 결합한다.
-
Stream 객체를 사용하여 filtering, Mapping, Sorting 과정을 거쳐서 정렬된다.
-
굉장히 객체의 분석이나 정렬이 쉬워진다.
a. 기본 형식
-
아래 Stream API 코드는 함수 모습처럼 보이지만 실제는 함수가 아니라 객체를 꽂아 넣는 것이라서 ‘functional-style 프로그래밍’이라고 부른다.
-
int 형식을 Stream에서 사용하기 위해서
IntStream
를 사용한다. -
이제는, a. 코드에 다른 함수를 꽂아 넣자!
package main.java.ex1.sort;
import java.util.Arrays;
import java.util.stream.IntStream;
public class Program{
public static void main(String[] args){
int[] ages = {10,32,34,21,23,25,46};
int[] result = IntStream
.of(ages)
.toArray();
System.out.println(Arrays.toString(result));
}
}
b. 응용 형식 1 : 조건문 추가
package main.java.ex1.sort;
import java.util.Arrays;
import java.util.stream.IntStream;
public class Program{
public static void main(String[] args){
int[] ages = {10,32,34,21,23,25,46};
int[] result = IntStream
.of(ages)
.filter(age->age>25)
.toArray();
System.out.println(Arrays.toString(result)); // 32, 34, 46 순서로 출력
}
}
c. 응용 형식 2 : 정렬 추가
- 굉장히 객체의 분석이나 정렬이 쉬워진다.
package main.java.ex1.sort;
import java.util.Arrays;
import java.util.stream.IntStream;
public class Program{
public static void main(String[] args){
int[] ages = {10,32,34,21,23,25,46};
int[] result = IntStream
.of(ages)
.filter(age->age>25)
.sorted()
.toArray();
System.out.println(Arrays.toString(result));
}
}
3) Stream API 객체 살펴보기
a. Mapping
- 일반적으로, 기존의 데이터 형식을 다른 모습으로 변환한다.
package main.java.ex1.sort;
import java.util.Arrays;
import java.util.stream.IntStream;
public class Program{
int[] ages = {10,32,34,21,23,25,46};
int[] result = IntStream
.of(ages)
.filter(age->age>25)
.sorted()
.map(age->age/10*10)
.toArray();
// 30, 40, 40 출력(나이가 30대, 40대로 변환)
System.out.println(Arrays.toString(result));
}
}
3) Stream 객체 심화 내용
- No Storage : 흐르는 강처럼 컨베이어 벨트와 같다. 데이터를 저장하지 않는다.
- Stream 객체 : 데이터의 원본을 꺼내기만 한다. 원본을 훼손하지 않는다.
- Laziness Seeking : 일련의 과정들이 나중에 다 최적화된다.
- Possibly unbounded : 저장소가 아니라서 크기를 유한할 때는 필요로하지 않는다.
- Consumable : 원본을 한번 꺼내서 사용하면 소비되고 사라진다.
- 즉, Stream 객체는 커다란 연산기라고 부른다.
4) Stream 객체 생성 방법
- Collection stream() : 원래, 콜렉션에서 스트림을 사용할 수 있다.
- Arrays.stream : 배열에서도 스트림을 사용할 수 있다.
- BufferedReader.lines() : 원격의 네트워크로 온 데이터도 스트림으로 읽을 수 있다.
5) Stream 객체 종류
- BaseStream 부모로부터 상속받아서 사용 가능하다.
- 스칼라 값을 가진 Stream 객체 종류 : IntStream, LongStream, DoubleStream
- 스칼라 값이라서 평균도 구할 수 있다.
- 참조 값을 가진 Stream 객체 종류 : Stream
- 참조 값이라서 연산은 불가능하다. 참조 값을 가진 Stream 객체는 일반 객체를 받을 수 있다.
6) Stream API 활용 : 파일 읽기
- Stream API로 외부 파일을 읽으면 이전의 FileInputStream을 이용하는 것보다 매우 편해진다.
package main.java.ex1.sort;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class Program{
public static void main(String[] args) throws IOException{
// ***** 3. Stream API 활용 2 : 파일 읽기(편하다.) *****
List<Exam> list = Files
.lines(Path.of("res/data.csv"))
.skip(1) // 첫 줄 넘기기
.map(line->{
String[] tokens = line.split(",");
if(tokens.length != 3)
throw new RuntimeException("인자의 개수를 잘못되었습니다.");
int kor = Integer.parseInt(tokens[0]);
int eng = Integer.parseInt(tokens[1]);
int math = Integer.parseInt(tokens[2]);
return new Exam(kor, eng, math);
})
.toList(); // 기존의 코드에서 map을 쓰면, 에러가 안 발생한다.
// JDK 8에서는 toList 대신에 collect를 사용한다.
// JDK 17에서 toList 사용한다.
System.out.println(list);
7) 리눅스 설치하는 과정
- 아래 과정은 Linux를 가상 환경에서 다운 받을 시, 나오는 설치 과정들이다.
- Mirror address : 원격의 Ubuntu 패키지 주소를 각 지역마다 나누어 패키지를 다운 받는 곳에서 다운 받는 속도를 빠르게 하기 위해 Mirroring하는 주소이다.
- Mount Point : 파일 저장 파티션
- ext4 파일 타입 : 리눅스의 파일 타입 설정
- Profile Setting : 리눅스는 관리자를 우리가 만들 수 있다.
- OpenSSH : 원격에서 관리할수 있는 최적화된 운영체제이다. 원격에서 접속할 수 있도록 하는 도구이다.
- Secure Shell이라고 부른다. 안정된 원격 shell이다.
8) 리눅스 이해하기
a. Shell :
- Shell의 기본 의미 : 기본적으로는 ‘사용자 인터페이스’를 의미한다.
- OS : 깔아놓은 ‘어플리케이션’을 모아서 ‘관리’하는 곳
- Shell 개념 : 운영체제가 실행하는 인터페이스, ‘운영체제를 감싸고 있는 것’이다. 원래 사전적으로 shell 뜻이 통한다.
- 정리** : ‘운영체제가 제공하는 인터페이스’이다.
- 사용자가 만질 수 있는 것은 Shell만 존재한다.
- 이제는 Shell을 통해 원격의 서버를 운영해보자!
- 가상환경에서 Linux를 켜고 기본 사용자의 컴퓨터 환경(원격 환경)에서 터미널로 접속하는 과정
b. Bash Shell :
-
원래는 Bourne Shell(본쉘)이다.
-
본쉘을 범용적으로 사용하기 위해 다시 만들어진 Bourne Again Shell인 ‘bash’를 이용한다.
c. ‘$ sign’,’#
(샵) sign’
-
$
sign : 일반적인 유저 계정 -
#
sign : 관리자 계정
d. sudo 명령어
-
sudo
: 일반 사용자가 일시적으로 관리자로 권한으로 사용할 수 있다. sudo su -
:-
만 쓰면 관리자 계정으로 변환된다.루팅
: 루트의 권한을 획득했다.(권리자 권한)
pwd
: 현재 워크 디렉토리(present work directory)
e. 원격(내 컴퓨터 터미널)에서 가상 컴퓨터(Virtual Box)로 접속하기!**
-
포트포워딩으로 설정 후 접속하기!!
-
접속 명령어** :
ssh
+" "
+id
+@
+ip주소
5. 예외처리, 리눅스 명령어 : 230519
1) 예외처리
- 라이브러리로 예외처리하기
throw new 함수명()
: 이것으로 사용자가 만든 예외로 예외처리하기!
a. 실습코드
- Program.java
package main.java.ex3.except;
public class Program {
// 여기서, throw는 자바의 런타임 환경에 예외 처리를 던지는 것이다.
public static void main(String[] args){
Calculator calc = new Calculator();
int result;
try {
// 예외처리를 실행하는애가 책임을 져서 여기서 모든 것을 끝낸다.
result = calc.add(1111, 2);
System.out.println(result);
} catch (천을넘는예외 e) {
System.out.println("죄송합니다. 입력에 오류가 있습니다. : 사용방법 합이 1000 이하");
// e.printStackTrace();
} catch (음수가되는예외 e) {
// e.printStackTrace();
System.out.println("죄송합니다. 입력에 오류가 있습니다. : 사용방법 합이 양수여야 합니다. ");
} finally{
System.out.println("마무리작업");
}
System.out.println("작업종료");
}
}
- Calculator.java
package main.java.ex3.except;
public class Calculator {
// 여기서, throw는 사용자가 만든 예외 처리 함수에 예외 처리를 던지는 것이다.
public static int add(int x, int y) throws 천을넘는예외, 음수가되는예외 {
int result = x+y;
if(result > 1000)
throw new 천을넘는예외();
if(result < 0)
throw new 음수가되는예외();
return result;
}
public static int sub(int x, int y) {
int result = x-y;
return result;
}
public static int multi(int x, int y) {
int result = x*y;
return result;
}
public static int div(int x, int y) {
int result = x/y;
return result;
}
}
- 천을넘는예외.java
package main.java.ex3.except;
// Exception을 상속 받아야 한다.
public class 천을넘는예외 extends Exception {
}
- 음수가되는예외.java
package main.java.ex3.except;
public class 음수가되는예외 extends Exception{
}
2) JDK 8 이후의 예외처리
- ‘try with resources’
- 리소스를 가지고 있는 try문
- 지역화되어서 다시 try-catch문을 이용하는 것을 막고 try문의 인자에 리소스를 담아서 1번만 선언해도 된다.
a. 실습코드
- Program2.java
package main.java.ex3.except;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class Program2 {
public static void main(String[] args){
// try with resources
try(
FileOutputStream fos = new FileOutputStream("res/data.txt");
PrintStream out = new PrintStream(fos);
){
out.println("Hello");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- data.txt
Hello
3) 리눅스 실행 및 명령어
a. 로컬에서 실행하기
- UTM에서 가상 환경에서 리눅스에 접속하고 맥북에서 iterm에서
ssh heon@아이피주소
를 입력해서 리눅스를 원격으로 접속하기
b. 리눅스에서 패키지 및 파일을 제거하는 방법
-
apt-get remove : 기본 패키지 파일 제거, 설정 파일은 남겨둔다.
-
apt-get purge : 설정파일도 같이 제거
c. 리눅스 파일 시스템의 구조
-
리눅스에서는 파일이랑 폴더가 같은 취급한다.
-
마운트 : 루트(“/”)에서 퍼져나가서 USB 파일을 리눅스의 브랜치에 붙이는 경우
-
레지스트리 : 어플리케이션이 가지고 있는 실행 설정
d. 리눅스 명령어
cd /
: root 경로로 이동
cd
: 현재 사용자 경로로 이동
e. 리눅스 디렉토리 역할 정리
- 디렉토리는 역할마다 정해진 디렉토리 구조를 무조건 지켜야하는 것은 적합하지는 않다.**
- 리눅스의 파일 경로를
bin
로 이동해서ls
를 입력해보면 연두색 파일명의 파일과 민트색 파일명의 파일이 있다.
- 연두색 파일명의 파일은 실행중인 파일들을 의미하고 민트색 파일의 링크 파일이며 바로가기를 의미한다.
- dev : 디스크의 폴더를 의미한다. 장치파일들이 저장되어 있는 디렉토리입니다.
- tty : 터미널을 의미한다.
- home : 현재 활동 중인 유저들의 id를 확인할수 있다.
- media : 현재는, 외장하드나 USB에 연결된 것이 없다. 외장하드 등과 연결되면 목록에서 목록 확인 가능!
- mnt : mnt 폴더명과 같은 역할을 한다. 예전에는 mnt 폴더라고 불리었다. 파일 시스템을 임시로 연결하는 디렉토리이다..
- opt : ‘사용자 어플리케이션’를 의미한다. 추가 패키지가 설치되는 디렉토리
- usr과 같은 역할을 지닌 폴더 구조이다.
- tmp : 실행중인 임시 데이터 저장 폴더.
- var : 환경 변수들의 모임.
f. 리눅스 디렉토리를 웹 개발에서 사용 방법 예시
- 톰캣의 사용/비사용 로그 파일을 var 폴더에 둔다.
- config 파일은 etc 폴더에 저장하면 된다.
4) 리눅스 : 파일 및 폴더 생성하고 지우기
- 바이너리 파일을 저장할 수 있는 폴더 만들기!
- ‘touch 파일명’ :
touch Hello.java
파일 만들기
- ‘rmdir’ : 디렉토리 지울 때, 사용하지만, 내부 폴더에 파일이 전부 지워져야 한다.
- ‘rm’ : 파일이나 디렉토리 지울 때, 사용한다.
- ‘rm -r 파일명’ : 강제로 지우기!
a. vi 편집기 사용하기(예전 버전 리눅스용 편집기)
- ‘vi HEllo.java’ : 자바 파일로 이동하기
- ‘i’ : 편집기에 텍스트 입력하기
- ‘dd’ : 편집기에 insert로 들어가기 전에 dd 명령어를 입력하면 텍스트를 1줄씩 지우기
- ‘dw’ : 편집기에 insert로 들어가기 전에 dd 명령어를 입력하면 텍스트의 글자 1개씩 지우기
b. nano 편집기 사용하기(최근 버전 리눅스용 편집기)
- 많이는 안 쓰이고 수정 시, 사용!!
- ‘nano Hello.java’ : 나노 편집기 사용하기!
- 나가는 방법 :
- 1) ctrl + x : 나가기
- 2) Y : 나가기 확정
- 3) Enter : 나가기!
- 외부 폴더에서 파일 내부 코드 읽기 : cat Hello.java
- 파일 복사하기 :
cp Hello.java Hello1.java
- ‘Hello.java’ 파일을 ‘Hello1.java’라는 파일로 추가로 생성하여 복붙한다.
- 파일명 바꾸기 :
mv Hello1.java Hello.java
- 같은 디렉토리에서 같은 디렉토리로 못가므로 파일명이 바뀐다.
- 파일 만들기 :
touch Hello.java
5) 파이프 라인 개념 :
- 한 쪽에서 결과를 다른 쪽에도 결과를 내보냄**
- 문자열 출력하기 : echo “Hello haha”
>
는 다른 파일에 내용을 대체하여 영향을 준다. :echo "Oh My God~~"
- 결과 : Oh My God
>>
는 다른 파일에 내용을 추가하여 영향을 준다. :echo "Oh My God~~@@"
- 결과 :
Oh My God~~@@ Oh My God
- 결과 :
- 파일 내부의 특정 문자열 검색하기 :
Hello.java || grep "MY"
- 파일 내부의 텍스트 중 “MY”라는 부분 찾기
cat Hello.java | More
: 전체 목록중 화면에 보여지는 부분 만 보여주고 나머지는 More로 대체해서 More 버튼을 눌러야지 전체 목록에 대한 나머지 부분을 볼 수 있다.
cat Hello.java | less
: 전체 목록 바로 보여주고 방향키의 위와 아래로 움직일 때
6) 리눅스 권한 :
- ‘rwX’ 개념 이용하기!