index.jsp파일에서 HelloServlet.java(Servlet)로 데이터를 요청하면, 서블릿에서 다시 JSP로 데이터를 json형태로 보내주는 것을 구현해보자.

즉, web에서 jsp는 client 측이고, servlet은 server 측이다.

jsp에서는 버튼을 누르면, $.ajax() 메서드를 통해 servlet으로부터 데이터를 받게 되고,

servlet에서 여러가지 type의 데이터를 json 형태로 바꿔 보내주도록 한다.

 

0. jsp에서 servlet으로 데이터 전송하기

먼저, servlet에서 getParameter()를 이용해 jsp로부터 매개변수를 넘겨받을 준비를 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package ajax3;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import dto.Human;
import net.sf.json.JSONObject;
 
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet doGet()");
    
        String id = req.getParameter("id");
        String pw = req.getParameter("pw");
        
        System.out.println("id: " + id + " pw: " + pw); // 서버가 클라이언트로부터 데이터를 받은 상태
        
    }    
    
}
 
cs

jsp 파일(index.jsp)은 다음과 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
 
<p id="demo"></p>
 
<button type="button">클릭</button>
 
<script>
$(function (){
    
    $("button").click(function(){
//         alert("button click");
        
        $.ajax({
            url:"./hello"// HelloServlet.java로 접근
            type: "get"// GET 방식
//             data: "id=abc&pw=123",
            data:{id:"abc", pw:"123"}, // json 방식으로 서블릿에 보낼 데이터
            success:function(data){
                alert("success");
            },
            error:function(){
                alert("error");
            }
            
        });
        
    });
    
});
 
 
</script>
 
</body>
</html>
cs

그 결과 다음과 같은 결과가 콘솔창에 나타난다.

이제 총 4가지의 타입의 객체를 서블릿에서 jsp로 보내주는 코드를 작성해본다.

위와 중복되는 부분은 제외하고 수정된 부분만 비교해보면

1. String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        String str = "안녕하세요";
        
        JSONObject jObj = new JSONObject();
        jObj.put("str", str); // key, value
        
        resp.setContentType("application/x-json; charset=utf-8");
        resp.getWriter().print(jObj);
        
        
    }    
    
}
cs

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
 
<p id="demo"></p>
 
<button type="button">클릭</button>
 
<script>
$(function (){
    
    $("button").click(function(){
//         alert("button click");
        
        $.ajax({
            url:"./hello"// HelloServlet.java로 접근
            type: "get"// GET 방식
            success:function(data){
                alert(data); // [object Object]
                alert(JSON.stringify(data)); // {"str":"안녕하세요"}
                alert(data.str); // 안녕하세요
            },
            error:function(){
                alert("error");
            }
            
        });
        
    });
    
});
 
 
</script>
 
</body>
</html>
cs

 

2. Object(Human.java)

Human.java 는 다음과 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package dto;
 
import java.io.Serializable;
 
public class Human implements Serializable{
    
    private String name;
    private int age;
    
    public Human() {
    }
 
    public Human(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    
}
 
cs

Human 객체를 참조하기 위한 변수 human을 servlet에서 생성하여 사용한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        // Object
        
        // dto
        
        Human human = new Human("홍길동"24);
        
        JSONObject jObj = new JSONObject();
        jObj.put("human", human);
        
        resp.setContentType("application/x-json; charset=utf-8");
        resp.getWriter().print(jObj);
        
        
    }    
    
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
 
<p id="demo"></p>
 
<button type="button">클릭</button>
 
<script>
$(function (){
    
    $("button").click(function(){
//         alert("button click");
        
        $.ajax({
            url:"./hello"// HelloServlet.java로 접근
            type: "get"// GET 방식
            success:function(data){
                alert(data); // [object Object]
                alert(JSON.stringify(data)); // {"human":{"age":24,"name":"홍길동"}}
                alert(data.human.name); // 홍길동
                alert(data.human.age); // 24
            },
            error:function(){
                alert("error");
            }
            
        });
        
    });
    
});
 
 
</script>
 
</body>
</html>
cs

 

3. list

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        // list
        
        List<Human> list = new ArrayList<Human>();
        list.add(new Human("홍길동"24));
        list.add(new Human("성춘향"16));
        list.add(new Human("홍두께"22));
        
        JSONObject jObj = new JSONObject();
        jObj.put("list", list);
        
        resp.setContentType("application/x-json; charset=utf-8");
        resp.getWriter().print(jObj);
        
        
    }    
    
}
cs

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
 
<p id="demo"></p>
 
<button type="button">클릭</button>
 
<script>
$(function (){
    
    $("button").click(function(){
//         alert("button click");
        
        $.ajax({
            url:"./hello"// HelloServlet.java로 접근
            type: "get"// GET 방식
            success:function(data){
                alert(data); // [object Object]
                alert(JSON.stringify(data)); // {"list":[{"age":24,"name":"홍길동"},{"age":16,"name":"성춘향"},{"age":22,"name":"홍두께"}]}
                alert(data.list[1].name); // 성춘향
                alert(data.list[0].age); // 24
            },
            error:function(){
                alert("error");
            }
            
        });
        
    });
    
});
 
 
</script>
 
</body>
</html>
cs

 

4. HashMap

다양한 자료형의 값들을 넣어서 json 형태로 바꾼 후 jsp로 보내주도록 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        // Map
        
        String str = "안녕하세요";
        
        List<Human> list = new ArrayList<Human>();
        list.add(new Human("홍길동"24));
        list.add(new Human("성춘향"16));
        
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("title", str);
        map.put("mylist", list);
        
        JSONObject jObj = new JSONObject();
        jObj.put("map", map);
        
        resp.setContentType("application/x-json; charset=utf-8");
        resp.getWriter().print(jObj);
        
    }    
    
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
 
<p id="demo"></p>
 
<button type="button">클릭</button>
 
<script>
$(function (){
    
    $("button").click(function(){
//         alert("button click");
        
        $.ajax({
            url:"./hello"// HelloServlet.java로 접근
            type: "get"// GET 방식
            success:function(data){
                alert(data); // [object Object]
                alert(JSON.stringify(data)); // {"map":{"mylist":[{"age":24,"name":"홍길동"},{"age":16,"name":"성춘향"}],"title":"안녕하세요"}}
                alert(data.map.mylist[1].name); // 성춘향
                alert(data.map.title); // 안녕하세요
            },
            error:function(){
                alert("error");
            }
            
        });
        
    });
    
});
 
 
</script>
 
</body>
</html>
cs

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Java > JSP, Servlet' 카테고리의 다른 글

JSP 내장 객체(out, request, session, pageContext)  (0) 2023.02.07
Servlet 정리  (0) 2023.02.06

자세한 설명은 다른 게시물에서 정리하도록 한다. 

간단하게 데이터를 추가, 출력, 삭제, 검색, 수정해보도록 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package main;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
 
public class MainClass2 {
    public static void main(String[] args) {
        
        // 클래스 객체
//        HashMap<Integer, String> hMap = new HashMap<Integer, String>();
        
        // 인터페이스 객체
        Map<Integer, String> hMap = new HashMap<Integer, String>();
        
        // 추가
        hMap.put(1"Korea");
        hMap.put(2"USA");
        hMap.put(3"Japan");
        hMap.put(4"England");
        hMap.put(5"Spain");
        
        System.out.println(hMap.size()); // 5
        
        // value가 존재하는 key에 새로운 value를 넣을 수 있다.
//        hMap.put(4, "Canada");
        
        // 모두 출력
        // iterator(반복자) 이용
        Iterator<Integer> it = hMap.keySet().iterator(); // 첫번째 key에 대해 주소 저장
        while(it.hasNext()) {
            Integer key = it.next();
            String value = hMap.get(key);
            System.out.println(key + ":" + value);
        }
        
        // 삭제
        String deleteVal = hMap.remove(5); // key가 5인 Spain 삭제
        System.out.println("삭제된 value: " + deleteVal); // Spain
        
        // 검색
//               value           key
        String value = hMap.get(1);
        
        // key값에 해당하는 값이 있는지 모를 때,
        boolean b = hMap.containsKey(2); // b == true
        if(b) {
            String val = hMap.get(2);
            System.out.println("value: " + val); // value: USA
        }
         
        // 수정
        hMap.replace(3"China");
        it = hMap.keySet().iterator();
        while(it.hasNext()) {
            Integer key = it.next();
            String val = hMap.get(key);
            System.out.println(key + ":" + val);
            /* 출력 결과
            1:Korea
            2:USA
            3:China
            4:England
            */
        }
        
        // HashMap의 장점: 다양한 객체를 저장할 수 있다.
        String str = "Hello";
        Integer in = 123;
        ArrayList<String> list = new ArrayList<String>();
        
        Map<String, Object> map = new HashMap<String, Object>();        
        map.put("문자열", str);
        map.put("숫자"in);
        map.put("목록", list);
        
        
        // TreeMap
        // HashMap과 다르게 자동sorting이 되어 있다. (key값을 기준으로 sorting) 
        TreeMap<Integer, String> tMap = new TreeMap<Integer, String>( hMap );
        
        Iterator<Integer> it2 = hMap.keySet().iterator();
        
        while(it2.hasNext()) {
            int k = it2.next();
            String v = tMap.get(k);
            System.out.println("key:" + k + " value:" + v);
        }
        /*
            key:1 value:Korea
            key:2 value:USA
            key:3 value:China
            key:4 value:England 
        */
        
        // TreeMap으로 오름차순 정렬이 되었다고 해서 원래 데이터인 hMap의 데이터는 변경되지 않는다.
    }
}
 
cs

 

 

출처: 권오흠 교수님

 

Theme. 클래스

한 사람의 "이름"과 "전화번호"는 항상 같이 붙어다녀야 하는 데이터이다. 

이 두가지 데이터를 서로 별개의 변수에 저장하면 우리가 이름 데이터를 옮길 때마다 전화번호 데이터도 따로 옮겨줘야 한다. 

만약 각 사람에 대해서 이름과 전화번호뿐만 아니라 주소, 이메일 등 여러 가지 데이터를 저장한다면 이 불편은 더 심해질 것이다. 

이렇듯 서로 관련있는 데이터들을 하나의 단위로 묶어두면 굉장히 편할 것이다. 이것이 클래스라는 개념이 등장하는 가장 기본적인 이유이다.

 

전화번호부 프로그램을 작성하면서 클래스에 대해 좀 더 알아보도록 한다.

먼저, 어떤 한 사람의 이름과 전화번호를 하나의 단위로 묶기 위해 "Person1"이라는 클래스를 만들었다.

주의할 것은,

클래스의 이름은 항상 대문자로 시작한다는 것과 클래스의 이름과 소스파일명의 이름이 일치해야한다는 점이다.

위 코드에서 name과 number는 각각 필드(field) 혹은 data member라고 한다.

 

클래스가 서로 관련 있는 데이터들을 하나의 단위로 묶기 위해 등장했다는 것. 이것이 첫번째 기본이다.

또 하나의 클래스에 대한 기본적인 내용은

클래스도 결국 하나의 "타입(type)"이라는 것이다. 마치, int, double 등 처럼.

다만, int, double처럼 Java가 미리 정해놓은 타입(primitive type, 기본형)이 아니라 사용자가 정의한 새로운 타입이라는 의미에서 "사용자 정의 타입"이라고 부르기도 한다.

 

만약, 어떤 한 사람의 이름, 전화번호 1쌍의 데이터를 "저장"하고 싶다면?

Person1이라는 클래스에 저장하는 것이 아니라 Person1 타입의 어떤 변수를 만들어 저장해야 한다. 

이는 마치 int(기본형 타입)에 4를 저장할 수 없는 것과 같은 논의다.

 

String 혹은 int 형 변수를 선언하고 사용하는 것처럼 Person1형 변수를 선언하고 사용하는 방법은 무엇일까?

Person1 first; // Person1 타입의 first라는 이름의 변수를 선언
first = new Person1(); // Person1의 객체(Person1 타입의 객체)를 만들고 변수 first가 그 객체의 주소(참조)를 저장

이를 다시 적어보면, 

Person1 first = new Person1(); 이다. 이때, new 연산자는 객체를 생성하는 역할을 한다.

클래스명 변수명 = new 클래스();

 

Theme. 프리미티브 타입과 클래스의 차이점

prmitive type(기본형)의 변수는 변수가 만들어지고 그 "변수 안"에 값이 저장된다.

하지만,

클래스는 변수가 만들어지고, 그 변수에 "생성된 클래스의 객체의 주소(참조)"가 저장된다. 그리고 "객체 안"에 값이 저장된다.

new 연산자를 통해 객체가 생성되고, "=" 연산자를 통해 객체의 주소(참조)가 변수에 저장되는 것. 그 결과 변수가 객체를 참조하게 된다.

그래서 클래스 타입의 변수를 선언하고, 클래스의 객체를 생성하여 변수에 그 객체의 주소를 저장하는 과정을 살펴보면,

다음과 같다.

정리하자면,

모든 프리미티브  타입의 변수는 보통 변수이다. 즉, 변수 자체에 값이 저장된다.

프리미티브 타입이 아닌 모든 변수는 참조 변수이다. 즉, 실제 데이터가 저장될 "객체"는 new 명령으로 따로 만들어야 하고, 참조변수에는 그 객체의 주소를 저장한다.

 

이어서, 아래 코드를 살펴보자.

위 코드에서 Person1 second = first; 의 의미에 대해 생각해보자. 

즉,

Person1 second; // Person1 타입의 second라는 이름의 새로운 참조변수를 선언

second = first; // 무슨 의미??

"=" 연산자의 의미를 생각하면 first라는 변수가 가지고 있는 값을 second라는 변수에 저장한다고 이해할 수 있을 것이다.

이때, first는 참조변수이므로 first가 가지고 있는 값은 "Person1의 객체의 주소"이다.

따라서, first가 가지고 있는 객체의 주소가 second라는 변수에 저장되는 것이다. 

결론적으로 참조변수인 first와 second 모두 Person1의 객체를 참조하게 된다. 

 

Person1[] members = new Person1[100]; 에서도 

members라는 배열의 각 칸은 Person1 타입의 변수이다.  즉, 참조변수이다.

그러므로 members[0] = first; 가 가능하다.(자료형이 같으므로)

이 식의 의미도 members[0]이 first가 가리키는 객체의 주소를 저장하게 된다는 것이다.

 

따라서, first, second, members[0], members[1] 모두 동일한 객체(Person1의 객체)를 참조하게 된다.

Theme. 참조변수 그리고 배열

여기서 잠시 "배열"의 자료형에 대해 자세히 알아보자.

int[] numbers = new int[8]; 에서

int[] 는 프리미티브 타입이 아니다! 다시 말해 int[] 타입의 변수인 numbers는 프리미티브 타입(기본형)의 변수가 아니다.

프리미티브 타입의 변수가 아닌 것들은 모두 참조 변수다.

따라서, 참조 변수인 numbers는 new를 통해 생성된 8칸을 가진 배열이 실제로 존재하는 주소를 저장할 뿐이다.

하지만, 배열의 각 칸은 int 타입의 프리미티브 변수이다.

또한,  이전 예제에서

Person1[] members = new Person1[8]; 을 보면

members도 참조 변수, 각 칸도 참조 변수이다.

Person1[] 타입의 배열, Person1 타입의 각 칸 모두 프리미티브 타입의 변수가 아니기 때문이다.

 

현재 Person1[] members = new Person1[8]; 으로 8칸짜리 members라는 배열이 생성되어 있다.

이때,

members[2].name = "John"; 

members[2].number = "010232432";

라고 저장하는 것은 불가능하다. 그 이유를 생각하기 위해 위 배열의 각 칸이 참조 변수인 것에 집중해보자.

참조 변수는 그 변수 안에 값을 저장할 수 없고, 값이 저장된 객체의 주소만을 저장할 수 있다.

따라서, 우선적으로 members[2]에 객체의 주소가 저장되어야 한다.

즉, 아래와 같이 작성해야 한다.

members[2] = new Person1(); // Person1의 객체를 생성하고 객체의 주소를 members[2]에 저장

members[2].name = "John";

members[2].number = "010232432";

 

참조변수와 관련하여 "값에 의한 호출"의 원리를 다시 한번 생각할 수 있다.

예를 들어, 함수를 호출하는 부분에서 bubbleSort(data, count); 라고 호출했다고 가정하자.

이때, data는 배열을 가리키는 참조변수(배열의 이름)이고, count는 int형 변수다.

호출된 메서드 부분에서 public static void bubbleSort(int[] data2, int n); 이라고 작성했다면,

data2를 버블 정렬했을 때, data도 버블 정렬이 됨을 확인할 수 있다. 이는 "값에 의한 호출" 때문인데, 자세히 살펴보면

  1. data와 data2는 모두 자료형이 int[]인, 즉 참조 변수이다.
  2. data와 data2가 저장하고 있는 배열(객체)의 주소는 동일하다.
  3. data2 배열에서 버블 정렬을 하게 되면, 동일한 배열의 주소를 저장하고 있는 data 배열 역시 버블정렬이 된다.

반면에 int형 프리미티브 타입의 변수인 count와 n은 각각 변수 안에 값이 저장되므로, 호출된 메서드에서 n의 값이 변한다 한들, count의 값은 변하지 않는다. 함수가 호출되었을 때, count의 값이 딱 한번! n 안의 값에 복사될 뿐이다.

 

처음 전화번호부를 만드는 것으로 돌아와서 클래스와 객체를 활용하여 작성한 코드를 살펴보자.

먼저, Person1 클래스는 다음과 같다.

 

1
2
3
4
5
6
7
8
9
10
11
package Section1;
 
public class Person1 {
    // 전화번호부 프로그램을 만들기 위한 클래스 생성
    // Person1 클래스는 한 사람의 이름과 전화번호를 하나의 단위로 묶는다.
    
    public String name;  // 각각 field 또는 data member라고 한다.
    public String number; 
 
}
 
cs

 

각 사람의 이름과 전화번호에 대한 정보가 들어있는 input.txt 파일은

John 237-645-2734
James 0283764823
Lee 2387462
Kim 28734628
Kwon 2738644283
Adam 23784628

의 정보가 들어있다.

 

이를 입력받아 전화번호부를 만든다면,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package Section1;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
 
public class Code02 {
 
    static int n = 0;
    static Person1[] members = new Person1[100]; // 배열의 길이 100
 
    public static void main(String[] args) {
        try {
            Scanner sc = new Scanner(new File("input.txt"));
            while (sc.hasNext()) {
 
                String nm = sc.next(); // 이름
                String nb = sc.next(); // 전화번호
                
                members[n] = new Person1(); // 배열의 각 칸이 Person1의 객체의 주소를 저장
                members[n].name = nm;
                members[n].number = nb;
                n++;
 
            }
            sc.close();
        } catch (FileNotFoundException e) {
            System.out.println("No data file");
        }
 
        for (int i = 0; i < n; i++) {
            System.out.println((i+1+ " name: " + members[i].name + "\tnumber: " + members[i].number);
        }
 
    }
 
}
 
cs

 

추가적으로 이름의 사전식 순서로 정렬하여 결과를 나타내보면, 다음과 같이 작성할 수 있다. (bubble sort 활용)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package Section1;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
 
public class Code03 {
 
    static Person1[] members = new Person1[100]; // 배열의 길이 100
    static int n = 0// 배열에 저장할 사람 수
 
    public static void main(String[] args) {
        try {
            Scanner sc = new Scanner(new File("input.txt"));
            while (sc.hasNext()) {
 
                String nm = sc.next(); // 이름
                String nb = sc.next(); // 전화번호
 
                members[n] = new Person1(); // 배열의 각 칸이 생성된 Person1의 객체의 주소를 저장
 
                members[n].name = nm;
                members[n].number = nb;
                n++// 1명을 읽을 때마다 사람 수 증가
 
            }
            sc.close();
        } catch (FileNotFoundException e) {
            System.out.println("No data file");
        }
        bubbleSort();
 
        for (int i = 0; i < n; i++) {
            System.out.println((i + 1+ " name: " + members[i].name + "\tnumber: " + members[i].number);
        }
 
    }
 
    // 매개변수가 없는 함수를 사용할 수 있는 이유는,
    // 사용하려는 배열과 사람 수가 main 함수가 아닌 클래스에 선언된 변수들이기 때문이다.
    private static void bubbleSort() {
 
        for (int i = n - 1; i > 0; i--) {
            for (int j = 0; j < i; j++) {
                if(members[j].name.compareToIgnoreCase(members[j+1].name) > 0) {
                    
                    Person1 tmp = members[j + 1];
                    members[j + 1= members[j];
                    members[j] = tmp;
                }
            }
        }
 
    }
 
}
 
cs

간단하게 사용하는 코드를 저장하고, 자세한 설명은 다른 게시물에 추가하도록 하자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<%-- 
 
    내장 객체 : 동적으로 할당(생성)하지 않고 (언제든지 바로) 사용할 수 있는 객체
    
    out: web으로 출력
    request
    response
    session
    pageContext
    application
         :
--%>
 
<h3>out(web에 출력)</h3>
 
<%
    String title = "hello jsp"
    out.println("<p>" + "hello jsp" + "</p>");
    out.println("<p>" + title + "</p>"); // = servlet java(html)
%>
 
<p><%=title %></p>
 
<h3>request</h3>
<%
// request(요청) : parameter 값을 취득, session을 접근, Object를 전송, encoding 설정 
// HttpServletRequest
// encoding 설정
request.setCharacterEncoding("utf-8");
 
// parameter 값을 취득
// 값 한개 취득(getParameter)
String name = request.getParameter("name");
// url에 "?name=홍길동"을 추가하면,
%>
<p><%=name %></p> <!-- 홍길동 -->
 
<%
// 값 여러개 취득(getParameterValues)
    String[] hobby = request.getParameterValues("hobby");
    // url에 ?hobby=book&hobby=game&hobby=running 추가
    for(int i = 0;i < hobby.length; i++){
    %>
        <p><%=hobby[i] %></p>
    <%    
    }
%>
 
<%
// session에 접근
session = request.getSession(); // 세션 생성 및 얻기
session.setAttribute("visited""1"); // 세션 이름: visited, 세션 값 1 저장
String str = (String)session.getAttribute("visited");
// request.getSession().getAttribute("세션명")의 리턴 타입이 Object이므로 캐스트 연산을 해야한다.
out.println("<h1>" + str + "</h1>"); // 1
 
%>
 
 
 
 
</body>
</html>
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package sample02;
 
import java.io.Serializable;
 
public class Human implements Serializable{
    String name;
    int age;
    
    public Human() {
    }
 
    public Human(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
    
    
    
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<%@page import="sample02.Human"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<h3>response</h3>
<%
// response: 이동
// response.sendRedirect("이동할 jsp 페이지"): 페이지 이동
// String name = "Tom";
// response.sendRedirect("defaultCopy.jsp?name=" + name + "&age=" + 15);
%>
 
<h3>pageContext</h3>
<
String name = "성춘향";
int age = 16;
 
// 마치 짐을 싸듯이...!
Human human = new Human(name, age);
// human이라는 참조변수가 가리키는 객체를 lady라는 이름으로 담아 보낼 준비
request.setAttribute("lady", human);
 
pageContext.forward("defaultCopy.jsp");
 
 
 
%>
 
</body>
</html>
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%@page import="sample02.Human"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<h1>defaultCopy.jsp</h1>
 
<%--
    String name = request.getParameter("name");
    int age = Integer.parseInt(request.getParameter("age"));
--%>
<%-- 
<p><%=name %></p> 
<p><%=age %></p> 
 --%>
 
<%
    Human human = (Human)request.getAttribute("lady");
%>
<p>이름:<%=human.getName() %></p>
<p>나이:<%=human.getAge() %></p>
 
</body>
</html>
cs

'Java > JSP, Servlet' 카테고리의 다른 글

JSP와 Servlet 사이에 데이터 주고 받기(json)  (0) 2023.02.08
Servlet 정리  (0) 2023.02.06

Theme. 서블릿(Servlet)이란?

서블릿(Servlet)이란,

클라이언트의 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술.

Dynamic Web Page를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술.

이라고 할 수 있다.

회사 소개 등과 같이 정적인 페이지의 경우와 다르게 동적인 페이지는 사용자의 다양한 요청에 대해 응답을 해줄 수 있어야 하는데, 서블릿은 이러한 요청과 응답의 흐름을 간단하고 체계적으로 다룰 수 있게 해주는 기술이다.

서블릿은 자바로 구현된 CGI라고 흔히 말한다.

cf) CGV(Common Gateway Interface)란?

 - CGI는 특별한 라이브러리나 도구를 의미하는 것이 아니고, 별도로 제작된 웹서버와 프로그램간의 교환방식이다. CGI방식은 어떠한 프로그래밍언어로도 구현이 가능하며, 별도로 만들어 놓은 프로그램에 HTML의 Get or Post 방법으로 클라이언트의 데이터를 환경변수로 전달하고, 프로그램의 표준 출력 결과를 클라이언트에게 전송하는 것이다. 즉, 자바 어플리케이션 코딩을 하듯 웹 브라우저용 출력 화면을 만드는 방법이다.

 

Theme. Servlet의 동작 원리

  1. 사용자(클라이언트)가 URL을 입력하면 HTTP Request가 Servlet Container로 전송한다.
  2. 요청을 전송받은 Servlet Container는 HttpServletRequest, HttpServletResponse 객체를 생성한다.
  3. web.xml(설정 파일)을 기반으로 사용자가 요청한 URL이 어느 서블릿에 대한 요청인지 찾는다.(매핑할 Servlet 확인)
  4. 서블릿 인스턴스가 없으면  init()메서드를 호출하여 생성하고, 서블릿 컨테이너에 스레드를 생성한다.
  5. 해당 서블릿에서 service메소드를 호출한 후 클리아언트의 GET, POST여부에 따라 doGet() 또는 doPost()를 호출한다.
  6. doGet() 또는 doPost() 메소드는 동적 페이지를 생성한 후 HttpServletResponse객체에 응답을 보낸다.
  7. 응답이 끝나면 destroy() 메서드를 실행하여 HttpServletRequest, HttpServletResponse 두 객체를 소멸시킨다.

 

Theme. Servlet의 주요 특징

  • 클라이언트의 Request에 대해 동적으로 작동하는 웹 애플리케이션 컴포넌트
  • HTML을 사용하여 Response 한다.
  • JAVA의 스레드를 이용하여 동작한다.
  • MVC 패턴에서의 컨트롤러로 이용된다.
  • HTTP 프로토콜 서비스를 지원하는 javax.servlet.http.HttpServlet 클래스를 상속받는다.
  • UDP보다 속도가 느리다.
  • HTML 변경 시 Servlet을 재 컴파일해야 하는 단점이 있다.

 

Theme. Servlet Container(서블릿 컨테이너)

 서블릿 컨테이너란 말 그대로 서블릿을 담고 관리해주는 컨테이너다. 서블릿 컨테이너는 클라이언트의 요청(Request)을 받아주고 응답(Response)할 수 있게, 웹서버와 소켓으로 통신하며 대표적인 예로 톰캣(Tomcat)이 있다. 톰캣은 실제로 웹 서버와 통신하여 JSP(자바 서버 페이지)와 Servlet이 작동하는 환경을 제공해준다. 이후 서블릿 예제들도 톰캣을 사용할 예정이다.

서블릿 컨테이너는 구현되어 있는 servlet 클래스의 규칙에 맞게 서블릿은 관리해주며 클라이언트에서 요청을 하면 컨테이너는 HttpServletRequest, HttpServletResponse 두 객체를 생성하며 post, get여부에 따라 동적인 페이지를 생성하여 응답을 보낸다.

 

HttpServletRequest
http프로토콜의 request정보를 서블릿에게 전달하기 위한 목적으로 사용하며 헤더 정보, 파라미터, 쿠키, URI, URL 등의 정보를 읽어 들이는 메서드와 Body의 Stream을 읽어 들이는 메서드를 가지고 있다.

HttpServletResponse
WAS(Web Application Server)는 어떤 클라이언트가 요청을 보냈는지 알고 있고, 해당 클라이언트에게 응답을 보내기 위한 HttpServleResponse 객체를 생성하여 서블릿에게 전달하고 이 객체를 활용하여 content type, 응답 코드, 응답 메시지 등을 전송한다.

 

init()

서블릿이 처음으로 요청될 때 초기화를 하는 메서드. 클래스를 new 해서 사용하듯 서블릿 클래스도 초기화해주어야 사용이 가능하다. 이렇게 초기화된 서블릿은 싱글톤으로 관리되어 다음에 한번 더 해당 서블릿 클래스를 호출하면 초기화가 다시 일어나는 것이 아니라 기존에 있던 서블릿 클래스를 호출하게 된다.

 

service()

서블릿 컨테이너가 요청을 받고 응답을 내려줄 때 필요한 서블릿의 service 메서드. Servlet interface를 구현한 HttpServlet 클래스의 doGet, doPost 같은 메서드들이 호출됩니다.

 

destroy()

더 이상 사용되지 않는 서블릿 클래스는 주기적으로 서블릿 컨테이너가 destory() 메서드를 호출하여 제거한다. 이렇게 제거된 서블릿은 service 메서드들에 해당하는 모든 스레드들이 종료되거나 사용시간이 오래되어 타임아웃된 경우에 이 클래스를 다시 사용하기 위해서는 init()을 다시 해주어야 한다.

 

< Servlet Container의 역할 >

1. 웹서버와의 통신 지원
서블릿 컨테이너는 서블릿과 웹서버가 손쉽게 통신할 수 있게 해준다. 일반적으로 우리는 소켓을 만들고 listen, accept 등을 해야하지만 서블릿 컨테이너는 이러한 기능을 API로 제공하여 복잡한 과정을 생략할 수 있게 해준다. 그래서 개발자가 서블릿에 구현해야 할 비지니스 로직에 대해서만 초점을 두게끔 도와준다.
 

2. 서블릿 생명주기(Life Cycle) 관리 
서블릿 컨테이너는 서블릿의 탄생과 죽음을 관리한다. 서블릿 클래스를 로딩하여 인스턴스화하고, 초기화 메소드를 호출하고, 요청이 들어오면 적절한 서블릿 메소드를 호출한다. 또한 서블릿이 생명을 다 한 순간에는 적절하게 Garbage Collection(가비지 컬렉션)을 진행하여 편의를 제공한다.

 

3. 멀티쓰레드 지원 및 관리 
서블릿 컨테이너는 요청이 올 때 마다 새로운 자바 쓰레드를 하나 생성하는데, HTTP 서비스 메소드를 실행하고 나면, 쓰레드는 자동으로 죽게된다. 원래는 쓰레드를 관리해야 하지만 서버가 다중 쓰레드를 생성 및 운영해주니 쓰레드의 안정성에 대해서 걱정하지 않아도 된다.
 

4. 선언적인 보안 관리 
서블릿 컨테이너를 사용하면 개발자는 보안에 관련된 내용을 서블릿 또는 자바 클래스에 구현해 놓지 않아도 된다. 일반적으로 보안관리는 XML 배포 서술자에다가 기록하므로, 보안에 대해 수정할 일이 생겨도 자바 소스 코드를 수정하여 다시 컴파일 하지 않아도 보안관리가 가능하다.

 

cf) Servlet 생명주기

 

  1. 클라이언트의 요청이 들어오면 컨테이너는 해당 서블릿이 메모리에 있는지 확인하고, 없을 경우 init()메소드를 호출하여 적재한다. init()메소드는 처음 한번만 실행되기 때문에, 서블릿의 쓰레드에서 공통적으로 사용해야하는 것이 있다면 오버라이딩하여 구현하면 된다. 실행 중 서블릿이 변경될 경우, 기존 서블릿을 파괴하고 init()을 통해 새로운 내용을 다시 메모리에 적재한다.
  2. init()이 호출된 후 클라이언트의 요청에 따라서 service()메소드를 통해 요청에 대한 응답이 doGet()가 doPost()로 분기된다. 이때 서블릿 컨테이너가 클라이언트의 요청이 오면 가장 먼저 처리하는 과정으로 생성된 HttpServletRequest, HttpServletResponse에 의해 request와 response객체가 제공된다.
  3. 컨테이너가 서블릿에 종료 요청을 하면 destroy()메소드가 호출되는데 마찬가지로 한번만 실행되며, 종료시에 처리해야하는 작업들은 destroy()메소드를 오버라이딩하여 구현하면 된다.

 

Theme. 여러 예제들 및 설명

 

서블릿 컨테이너로 톰캣을 사용하고, 이클립스에서 Dynamic Web Project를 생성하고, html, java 파일(서블릿 작성), web.xml 파일을 만든다.

 

먼저, web.xml 파일에서 서블릿을 정의하는 방법에 대해 알아보자.

web.xml은 서버가 시작될 때 웹서버가 사용하는 (환경)설정 파일인데, 웹 애플리케이션 서비스 실행에 관한 전반적인 내용을 정의하는 환경설정 파일이다.

서버에서 서블릿 실행에 관한 정보를 설정할 때 web.xml에 <servlet> 태그로 설정한다. 

아래와 같이 작성할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
  <display-name>sample01</display-name>
  
<!--  설정 부분 --> 
  <servlet>
      <servlet-name>hello</servlet-name> 
      <servlet-class>sample01.HelloServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/location</url-pattern>
  </servlet-mapping>
  
</web-app>
cs

<servlet> 태그에서,

<servlet-name> 태그: 해당 서블릿을 참조할 때 사용할 이름

<servlet-class> 태그: 서블릿으로 사용할 서블릿 클래스 ( "패키지명.클래스명" 형식으로 작성)

 

<servlet-mapping> 태그에서,

<servlet-name> 태그: 매핑할 서블릿의 이름

<url-pattern> 태그: 매핑할 url 패턴

 

이제, doGet()과 doPost()이 포함된 서블릿을 작성해보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package sample01;
 
import java.io.IOException;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class HelloServlet extends HttpServlet{
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet doGet");
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet doPost");
    }
    
    
}
 
cs

그리고, index.html 파일은 다음과 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<!--  
        Servlet = Server + Applet
        
        get:     parameter가 오픈
        post:    parameter가 보이지 않음(보안의 측면)
-->
 
<form action="location" method="get"> <!-- doGet으로! -->
    <input type="submit" value="get이동"> <!-- doPost으로! -->
</form>
<br>
<form action="location" method="post">
    <input type="submit" value="post이동">
</form>
<br>
 
<a href="location">a tag move</a> <!-- get  -->
<br><br>
 
<button type="button" onclick="move()">JS 이동</button>
 
<!-- get  -->
<script type="text/javascript"> 
function move(){
    location.href="location"
}
 
 
 
</script>
 
</body>
</html>
cs

<form> 의 action 속성, <a>의 href 속성, location.href=""를 이용하여 이동하였다.

 

2번째 예제) 사용자가 입력한 값을 넘겨받아 html 문서를 만듦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
  <display-name>sample02</display-name>
  
  <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>sample02.HelloServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/hello</url-pattern>
  </servlet-mapping>
  
  <servlet>
      <servlet-name>sample</servlet-name>
      <servlet-class>sample02.SampleServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
      <servlet-name>sample</servlet-name>
      <url-pattern>/sample</url-pattern>
  </servlet-mapping>
  
  
  
  
  
</web-app>
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package sample02;
 
import java.io.IOException;
import java.io.PrintWriter;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class HelloServlet extends HttpServlet{
 
    @Override       // request.getParameter("id") -> JSP
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        /*
              servlet -> java코드에 html 태그를 넣어서 사용 --> Controller로 사용
              
              JSP -> html 코드에 java가 섞여있다.
        */
        
//        아래 코드와 같이 사용하지는 않는다. 너무 복잡하니까
        
        resp.setContentType("text/html; charset=utf-8");
        
        PrintWriter pw = resp.getWriter();
        
        pw.println("<html>");
        
        pw.println("<head>");
            pw.println("<title>제목입니다</title>");
        pw.println("</head>");
        
        pw.println("<body>");
            pw.println("<h1>Welcome to HelloServlet</h1>");
            pw.println("<p>hello Servlet</p>");
            
            pw.println("<a href='index.html'>index.html</a>");
        pw.println("</body>");
        
        pw.println("</html>");
        
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
    }
    
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package sample02;
 
import java.io.IOException;
import java.io.PrintWriter;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class SampleServlet extends HttpServlet {
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        String name = req.getParameter("name");
        String sAge = req.getParameter("age");
        int age = Integer.parseInt(sAge);
        
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter pw = resp.getWriter();
 
        pw.println("<html>");
 
        pw.println("<head>");
        pw.println("<title>제목입니다</title>");
        pw.println("</head>");
 
        pw.println("<body>");
        
        pw.println("<p>name: " + name + "</p>");
        pw.println("<p>age: " + age + "</p>");
         
        pw.println("</body>");
 
        pw.println("</html>");
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        req.setCharacterEncoding("utf-8"); // 한글 깨지는 것 해결방법
        
        String name = req.getParameter("name");
        String sAge = req.getParameter("age");
        int age = Integer.parseInt(sAge);
        
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter pw = resp.getWriter();
 
        pw.println("<html>");
 
        pw.println("<head>");
        pw.println("<title>제목입니다</title>");
        pw.println("</head>");
 
        pw.println("<body>");
        
        pw.println("<p>name: " + name + "</p>");
        pw.println("<p>age: " + age + "</p>");
         
        pw.println("</body>");
 
        pw.println("</html>");
    }
 
}
 
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<form action="hello" method="get">
    <input type="submit" value="이동">
</form>
 
<br><br>
 
<form action="sample" method="get">
이름:<input type="text" name="name"><br>
나이:<input type="text" name="age"><br><br>
<input type="submit" value="sample get로 이동">
</form>
<br><br>
 
<form action="sample" method="post">
이름:<input type="text" name="name"><br>
나이:<input type="text" name="age"><br><br>
<input type="submit" value="sample post로 이동">
</form>
 
</body>
</html>
cs

3번째 예제) 2번째 예제와 같은 상황인데, 값을 여러개 입력받아 html 문서를 만듦

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
  <display-name>sample03</display-name>
  
  <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>sample03.HelloServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/hello</url-pattern>
  </servlet-mapping>
  
  
  
</web-app>
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package sample03;
 
import java.io.IOException;
import java.io.PrintWriter;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class HelloServlet extends HttpServlet {
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doProc(req, resp);
 
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doProc(req, resp);
 
    }
 
    public void doProc(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 
        String[] hobby = req.getParameterValues("hobby"); // 2개 이상의 데이터를 받기 위함
        String[] url = req.getParameterValues("url"); // 2개 이상의 데이터를 받기 위함
 
        resp.setContentType("text/html; charset=utf-8");
 
        PrintWriter pw = resp.getWriter();
 
        pw.println("<html>");
 
        pw.println("<head>");
        pw.println("<title>제목입니다</title>");
        pw.println("</head>");
 
        pw.println("<body>");
 
        pw.println("<p>취미</p>");
        if(hobby != null)
            for (int i = 0; i < hobby.length; i++) {
                pw.println("<p>" + hobby[i] + "</p>");
            }
 
        pw.println("<p>사이트</p>");
        if(url != null)
        for (int i = 0; i < url.length; i++) {
            pw.println("<p>" + url[i] + "</p>");
        }
 
        pw.println("</body>");
 
        pw.println("</html>");
 
    }
 
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
 
<form action="hello" id="frm">
<h3>취미</h3>
<input type="checkbox" name="hobby" value="picture">그림그리기
<input type="checkbox" name="hobby" value="game">게임하기
<input type="checkbox" name="hobby" value="movie">영화보기
<br><br>
 
<h3>좋아하는 사이트</h3>
<select name="url" multiple="multiple">
    <option value="naver">네이버</option>
    <option value="google">구글</option>
    <option value="zum"></option>
</select>
 
<button type="button">send</button>
 
</form>
 
<script type="text/javascript">
$(document).ready(function(){
    
    $("button").click(function(){
        $("#frm").submit(); // action 위에서 적어줬으니까
//         $("#frm").attr("action", "hello").submit(); // action 위에서 안 적어줬을 때
        
    });
});
 
 
</script>
 
 
</body>
</html>
cs

 

4번째 예제) 사용자로부터 입력을 받아 HelloServlet.java, WorldServlet.java 파일을 모두 거쳐 응답해줌

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
  <display-name>sample04</display-name>
  
  <!-- HelloServelt -->
  <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>sample04.HelloServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/hello</url-pattern>
  </servlet-mapping>
  
  <!-- WorldServelt -->
  <servlet>
      <servlet-name>world</servlet-name>
      <servlet-class>sample04.WorldServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
      <servlet-name>world</servlet-name>
      <url-pattern>/world</url-pattern>
  </servlet-mapping>
  
  
  
</web-app>
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package dto;
 
import java.io.Serializable;
 
public class Human implements Serializable {
    
    private String name;
    private int age;
    private String address;
    
    public Human() {}
 
    public Human(String name, int age, String address) {
        super();
        this.name = name;
        this.age = age;
        this.address = address;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public String getAddress() {
        return address;
    }
 
    public void setAddress(String address) {
        this.address = address;
    }
    
    
    
    
    
    
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package sample04;
 
import java.io.IOException;
import java.net.URLEncoder;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import dto.Human;
 
public class HelloServlet extends HttpServlet{
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doProc(req, resp);
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doProc(req, resp);
    }
    
    public void doProc(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet doProc()");
        
        req.setCharacterEncoding("utf-8");
        
        String name = req.getParameter("name");
        int age = Integer.parseInt(req.getParameter("age"));
        String address = req.getParameter("address");
        
        /*
         * 첫번째 방법(response 소속의 sendRedirect 사용)
        // 한글의 경우에는 인코딩을 처리해 주어야 한다.
        String ename = URLEncoder.encode(name);
        String eaddress = URLEncoder.encode(name);
        
//        resp.sendRedirect("world"); // 맵핑된 world로 이동
        
        resp.sendRedirect("world?name=" + ename + "&age=" + age + "&address=" + eaddress);
     
        */
        
//        두번째 방법(RequestDispatcher 소속의 forward 사용)
        Human h = new Human(name, age, address);
        
        // 짐 싸!
        req.setAttribute("human", h); // h 객체를 "human"이라는 이름으로!
        
        // 이동
        req.getRequestDispatcher("world").forward(req, resp);
//        resp.sendRedirect("world"); --> 단순이동
        
        
        
        
        
        
    }
    
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package sample04;
 
import java.io.IOException;
import java.io.PrintWriter;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import dto.Human;
 
public class WorldServlet extends HttpServlet{
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doProc(req, resp);
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doProc(req, resp);
    }
 
    public void doProc(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /* 첫번째 방법에서 사용
        String name = req.getParameter("name");
        int age = Integer.parseInt(req.getParameter("age"));
        String address = req.getParameter("address");
        */
        
        // 두번째 방법
        // 짐을 푼다
        Human human = (Human)req.getAttribute("human");
        
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html; charset=utf-8");
 
        PrintWriter pw = resp.getWriter();
 
        pw.println("<html>");
 
        pw.println("<head>");
        pw.println("<title>World입니다</title>");
        pw.println("</head>");
 
        pw.println("<body>");
        
        pw.println("<h1>World</h1>");
        /*
        pw.println("<p>이름: " + name + "</p>");
        pw.println("<p>나이: " + age + "</p>");
        pw.println("<p>주소: " + address + "</p>");
        */
        
        pw.println("<p>이름: " + human.getName() + "</p>");
        pw.println("<p>나이: " + human.getAge() + "</p>");
        pw.println("<p>주소: " + human.getAddress() + "</p>");
        
        
        pw.println("</body>");
 
        pw.println("</html>");
 
    }
    
    
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<form action="hello" method="get">
이름:<input type="text" name="name"><br>
나이:<input type="text" name="age"><br>
주소:<input type="text" name="address"><br>
<input type="submit" value="전송">
</form>
 
</body>
</html>
cs

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

자료 참고 및 출처: https://mangkyu.tistory.com/14

 

[JSP] 서블릿(Servlet)이란?

1. Servlet(서블릿) 서블릿을 한 줄로 정의하자면 아래와 같습니다. 클라이언트의 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술 간단히 말해서,

mangkyu.tistory.com

https://coding-factory.tistory.com/742

 

[Web] 서블릿(Servlet)이란 무엇인가? 서블릿 총정리

서블릿(Servlet)이란? 서블릿이란 Dynamic Web Page를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술입니다. 웹을 만들때는 다양한 요청(Request)과 응답(Response)이 있기 마련이고 이 요

coding-factory.tistory.com

 

출처: 권오흠 교수님

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package CH1;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;
 
public class Code22 {
    
    static String[] words = new String[100000]; // 단어들의 목록
    static int[] count = new int[100000]; // 각 단어들의 등장 횟수
    static int n = 0// 목록에 저장된 단어의 개수
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(true) {
            
            System.out.println("read, find, saveas, exit 중 선택하시오.");
            System.out.print("$ ");
            String command = sc.next(); // 명령어 입력(4가지 중 하나)
            if(command.equals("read")) { // 텍스트 파일을 읽고 인덱스 목록을 만든다.
                String filename = sc.next();
                makeIndex(filename);
            }
            else if(command.equals("find")) { // 특정 단어가 몇번 나오는지 횟수를 출력
                String str = sc.next();
                int index = findWord(str);
                if(index != -1) {
                     System.out.println(words[index] + "(은)는 " + count[index] + "번 나왔습니다.");
                }
                else {
                    System.out.println(words[index] + "(은)는 존재하지 않습니다.");
                }
            }
            else if(command.equals("saveas")) { // 인덱스 목록을 파일로 저장
                String filename = sc.next();
                saveAs(filename);
            }
            else if(command.equals("exit")) { // 종료
                break;
            }
            
        }
        sc.close();
        
        // 문자열과 나온 횟수 확인
        for (int i = 0; i < n; i++) {
            System.out.println(words[i] + " " + count[i]);
        }
        
        
    }
    
    // 텍스트 파일을 읽고 인덱스를 만드는 함수
    static void makeIndex(String fileName) {
        try {
            Scanner inFile = new Scanner(new File(fileName)); // 파일로부터 입력 받음
            // 파일을 처음부터 끝까지 모두 읽는다(파일에 등장하는 모든 단어들을 확인하기 위해) 
            while(inFile.hasNext()) {
                String str = inFile.next();
                addWord(str);
            }
            
            inFile.close();
        } catch (FileNotFoundException e) { // fileName의 파일이 존재하지 않는 경우
            System.out.println("No file");
            return;
        } 
    }
    
    // 어떤 문자열이 목록에 이미 있는지 없는지 확인하고 -> 이미 있으면 제외 없으면 추가
    static void addWord(String str) {
        int index = findWord(str); // returns -1 if not found
        if (index != -1) { // found
            count[index]++;
        }
        else { // not found
            words[n] = str; // 배열 내 값이 null인 것들(그 전까지는 문자열들로 채워져있고) 중 첫 번째에 문자열을 저장
            count[n] = 1// 새로운 문자열을 추가하면서 동시에 등장 횟수 1로 지정
            n++// 목록에 저장된 단어의 개수 +1
        }
    }
    
    // words 배열에 어떤 문자열이 존재하지는지, 존재한다면 그 위치(index)를 반환
    static int findWord(String str) {
        for (int i = 0; i < n; i++) {
            if(words[i].equalsIgnoreCase(str)) { // 대소문자 구분하지 않고 동일한지 비교
                return i; // 존재한다면 그 위치를 반환
            }
        }
        return -1// 존재하지 않는다면 -1을 반환
    }
    
    // 파일로 저장(출력 = 파일 쓰기)
    static void saveAs(String filename){
        try {
            PrintWriter pw = new PrintWriter(new FileWriter(filename));
            for (int i = 0; i < n; i++) {
                pw.println(words[i] + " " + count[i]);
            }
            pw.close();
        } catch (IOException e) {
            System.out.println("파일 저장 실패");
            return;
        }
    }
    
 
}
 
cs

위 코드를 실행시켜 보자.

먼저, 사전에 sample.txt라는 파일을 만들어 두었고, "saveas"를 입력하여 문자열(텍스트)을 파일로 출력(저장)할 때는 output.txt라는 파일을 만들어 저장하도록 한다.

sample.txt 파일의 내용은 다음과 같다. 

sample.txt
0.00MB

이제, read, find, saveas, exit를 사용한 결과를 살펴보자.

output.txt는

output.txt
0.00MB

 

결과를 살펴봤을 때, 발생할 수 있는 문제점들이 있다.

1. 소수점, 쉼표 등의 특수기호가 단어에 포함되는 점

2. 숫자 등이 단어로 취급된다는 점

3. 단어들이 알파벳 순으로 정렬되지 않았다.

이를 해결하는 방법은??

 

해결방법을 찾기 위해 String 클래스의 기본 메서드들을 살펴보자.

 

Theme. String 클래스 기본 메서드

 

1. 문자열 동일성 검사

A라는 문자열이 B라는 문자열과 동일한 문자열인지를 알아보고 싶다?

A.equals(B);  즉, "equals( String )"

equals 메서드는  boolean 값을 반환한다.

만약, 대소문자 구분없이 문자열이 서로 동일한 문자열인지를 알아보고 싶다면?

A.equalsIgnoreCase(B);

 

간단한 예제를 살펴보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package CH1;
 
public class StringClass {
    public static void main(String[] args) {
        String str1 = "java";
        String str2 = "Java";
        boolean b1 = str1.equals(str2);
        boolean b2 = str1.equalsIgnoreCase(str2);
 
        System.out.println(b1); // false
        System.out.println(b2); // true
 
    }
}
 
cs

 

2. 문자열 사전식 순서

A라는 문자열과 B라는 문자열 중에 어떤 문자열이 사전식 순서로 더 앞서는지를 알고 싶다면?

A.compareTo(B); 즉, "compareTo( String )"

compareTo 메서드는 int값을 반환한다.

이때, 결과를 잘 해석해야 한다.

만약 A.compareTo(B)의 결과가 0보다 작다면 A가 B보다 사전식 순서가 앞선다는 의미이고, 0보다 크다면 B가 A보다 사전식 순서가 앞선다는 의미이다. 완전히 동일한 문자열일 경우에는 결과가 0이다.

결과는 자릿수의 차이에 따라 다양한 정수로 나오지만 0보다 작냐 크냐로 이해하는 것이 좋다.

왜냐하면 tip이라면 tip일 수있는 것이 A.compareTo(B)의 결과가 0보다 작으면( < 0 ) A < B(A가 앞선다)로 기억하고,

0보다 크면 ( > 0 ) A > B(B가 앞선다)로 기억하면 되기 때문이다.

추가로 compareToIgnoreCase ( String ) 메서드도 존재하는데, 이는 대소문자를 무시하고 사전식 순서를 비교하는 것이다.

 

예제를 통해 알아보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package CH1;
 
public class StringClass {
    public static void main(String[] args) {
        String str1 = "apple";
        String str2 = "banana";
 
        int result1 = str1.compareTo(str2);
        System.out.println(result1);// -1
        int result2 = str2.compareTo(str1);
        System.out.println(result2); // 1
 
    }
}
 
cs

 

3. 문자열 길이

어떤 문자열(A)의 길이를 알고 싶다면?

A.length()

( 주의할 것은 배열의 길이를 알고 싶을 때는 array.length로 차이가 있다는 것이다. )

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package CH1;
 
public class StringClass {
    public static void main(String[] args) {
        String str1 = "apple";
        String str2 = "banana";
 
        int len1 = str1.length();
        int len2 = str2.length();
 
        System.out.println(len1); // 5
        System.out.println(len2); // 6
    }
}
 
cs

 

4. 특정 위치의 문자

ABCDEFG라는 문자열이 있을 때, 3번째 위치에 존재하는 문자 C를 반환하고 싶다면?

charAt( int ) 메서드를 이용한다.

이때, 위치 인덱스는 0부터 시작한다. 따라서, 3번째 위치는 charAt(2)를 통해 값을 반환할 수 있다.

또한, charAt 메서드의 반환값의 자료형은 char type이다.

 

1
2
3
4
5
6
7
8
9
10
package CH1;
 
public class StringClass {
    public static void main(String[] args) {
        String str = "ABCDEFG";
        char ch = str.charAt(2);
        System.out.println(ch); // C
    }
}
 
cs

 

5. 지정한 문자의 위치 검색

ABCDEFG라는 문자열이 있을 때, C가 존재하는 위치를 알고 싶다면?

indexOf ( char ) 메서드를 이용한다.

이때, 위치 인덱스는 0부터 시작한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
package CH1;
 
public class StringClass {
    public static void main(String[] args) {
        String str = "ABCDEFG";
        int index1 = str.indexOf("C");
        int index2 = str.indexOf("F");
        
        System.out.println(index1); // 2
        System.out.println(index2); // 5
    }
}
 
cs

 

6. 지정한 범위의 부분 문자열

 ABCDEFG라는 문자열에서 CDE라는 문자열만 떼어내서 반환하고 싶다면?

substring( int, int ) 메서드를 사용한다. 

주의할 것이 있다! 해당 메서드를 보다 자세히 살펴보면,

substring( 시작하는 인덱스, 끝나는 인덱스 + 1 ) 이다. 

예를 들어, substring( 1, 4 )의 경우에는 인덱스 기준 1부터 3까지의 위치에 존재하는 문자열을 반환하게 된다.

 

1
2
3
4
5
6
7
8
9
10
11
package CH1;
 
public class StringClass {
    public static void main(String[] args) {
        String str1 = "ABCDEFG";
        String str2 = str1.substring(2,5); 
        
        System.out.println(str2); // CDE
    }
}
 
cs

 

7. 문자열 자르기

012-3456-789라는 문자열에서 기호가 아닌 문자열만을 잘라내고 싶다면?

구분자(delimiter)를 기준으 split()메서드를 활용한다.

split() 메서드는 그 결과를 String[] 타입의 배열에 각각 저장한다.  

 

 만약, "."을 기준으로 split을 하고 싶을 때, split( "." )처럼 작성하면 안된다.  왜냐하면 split 함수에서 구분자를 정규표현식으로 받기 때문이다. 이 경우 split("\\.")으로 작성해주어야 한다.

"."뿐만 아니라 아래의 특수문자들도 \\를 붙여야 한다.

 

| ? * ( ) [ ] { } \

 

 

위와 같은 구분자뿐만 아니라 공백을 기준으로도 split을 할 수 있는데, 

 

 

 

 

이제, 위에서 언급한 문제점들을 해결해보자.

trimming이라는 함수를 만들어서, 단어의 앞뒤에 존재하는 특수문자 등을 제거하도록 한다. 

이 trimming이라는 함수는 단어를 목록에 저장하기 전에 실행되어야 하므로,

makeIndex 함수 안에서 addWord 함수 이전에 실행되도록 위치시킨다.

 

trimming 함수에서는 어떤 문자열이 있을 때,

1. 앞에서부터 알파벳이 아닌 문자열들이 존재하는 동안

2. 뒤에서부터 알파벳이 아닌 문자열들이 존재하는 동안

while문을 돌리도록 한다.

이때, 어떤 char이 알파벳인지 아닌지를 판별하기 위해, isLetter( char )라는 메서드를 사용하도록 한다. 반환값의 자료형은 boolean이다.

trimming 함수 부분을 작성한 코드를 살펴보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static String trimming(String str) {
        // i는 문자열의 앞에서부터, j는 뒤에서부터
        int i = 0, j = str.length() - 1;
        while( i < str.length() && !Character.isLetter(str.charAt(i))) {
            i++;
        }
        while( j >= 0 && !Character.isLetter(str.charAt(j))) {
            j--;
        }
        // 알파벳이 존재하는 문자열의 시작 위치(i), 끝 위치(j)를 얻게됨
        if(i > j) // substring에서 오류가 발생하므로
            return null
        else
            return str.substring(i, j + 1); // substring은 [  )
    }
cs

 

이제, 단어들을 알파벳 순으로 정렬하는 과정을 작성해보자. 

 

이미 정렬되어 있는 목록의 경우 기존에 존재하던 단어가 목록에 추가될 때는 정렬을 해줄 필요가 없다.

따라서, 새로운 문자열이 추가되는 케이스에 대하여 정렬하는 것에 초점을 맞춘다.

 

정리하자면, 정렬되어 있는 목록에 새로운 단어를 추가하는데, 동시에 정렬이 되도록 하고자 한다.

단어를 추가하기 위해 목록의 뒤에서부터 사전식 순서를 비교하도록 한다. 앞에서부터 비교하면 새로운 단어를 넣기 위해 배열 전체를 한칸씩 뒤로 옮겨야하므로 뒤에서부터 비교하는 것이 더 효율적이기 때문이다.

즉, ordered list에 어떤 값을 insert할 때는 뒤에서부터 비교하면서 insert하는 것이 좋다.

이에 대한 코드는

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 어떤 문자열이 목록에 이미 있는지 없는지 확인하고 -> 이미 있으면 제외 없으면 추가
    static void addWord(String str) {
        int index = findWord(str); // returns -1 if not found
        if (index != -1) { // found
            count[index]++;
        }
        else { // not found
            int i = n-1;
            while(i >=0 && words[i].compareTo(str) > 0) {
                words[i+1= words[i];
                count[i+1= count[i];
                i--;
            }
            words[i+1= str; // 배열 내 값이 null인 것들(그 전까지는 문자열들로 채워져있고) 중 첫 번째에 문자열을 저장
            count[i+1= 1// 새로운 문자열을 추가하면서 동시에 등장 횟수 1로 지정
            n++// 목록에 저장된 단어의 개수 +1
        }
    }
cs

이때, "인덱스"를 이해하는 데 부가적인 설명을 덧붙이자면,

위 코드에서 str이 words[5]와 words[6] 사이에 들어가야 정렬이 되는 상황이라고 가정해보자.

순차적으로,

1. i == 6일때, words[6]이 한칸 뒤로 이동

2. i--로 i == 5가 됨

3. i == 5일때, words[5]는 뒤로 이동하지 않음

4. str은 words[6] 자리. 즉, i == 6의 위치에 들어가게 됨. 이때 i == 5이므로 i+1 위치에 str이 들어가는 것

 

최종적으로 완성된 코드는 다음과 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package CH1;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;
 
public class Code23 {
    
    static String[] words = new String[100000]; // 단어들의 목록
    static int[] count = new int[100000]; // 각 단어들의 등장 횟수
    static int n = 0// 목록에 저장된 단어의 개수
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(true) {
            
            System.out.println("read, find, saveas, exit 중 선택하시오.");
            System.out.print("$ ");
            String command = sc.next(); // 명령어 입력(4가지 중 하나)
            if(command.equals("read")) { // 텍스트 파일을 읽고 인덱스 목록을 만든다.
                String filename = sc.next();
                makeIndex(filename);
            }
            else if(command.equals("find")) { // 특정 단어가 몇번 나오는지 횟수를 출력
                String str = sc.next();
                int index = findWord(str);
                if(index != -1) {
                     System.out.println(words[index] + "(은)는 " + count[index] + "번 나왔습니다.");
                }
                else {
                    System.out.println(words[index] + "(은)는 존재하지 않습니다.");
                }
            }
            else if(command.equals("saveas")) { // 인덱스 목록을 파일로 저장
                String filename = sc.next();
                saveAs(filename);
            }
            else if(command.equals("exit")) { // 종료
                break;
            }
            
        }
        sc.close();
        
        // 문자열과 나온 횟수 확인
        for (int i = 0; i < n; i++) {
            System.out.println(words[i] + " " + count[i]);
        }
        
        
    }
    
    // 텍스트 파일을 읽고 인덱스를 만드는 함수
    static void makeIndex(String fileName) {
        try {
            Scanner inFile = new Scanner(new File(fileName)); // 파일로부터 입력 받음
            // 파일을 처음부터 끝까지 모두 읽는다(파일에 등장하는 모든 단어들을 확인하기 위해) 
            while(inFile.hasNext()) {
                String str = inFile.next();
                
                String trimmed = trimming(str);
                if(trimmed != null) {
                    String t = trimmed.toLowerCase(); // 소문자로
                    addWord(t);
                }
            }
            
            inFile.close();
        } catch (FileNotFoundException e) { // fileName의 파일이 존재하지 않는 경우
            System.out.println("No file");
            return;
        } 
    }
    
    static String trimming(String str) {
        // i는 문자열의 앞에서부터, j는 뒤에서부터
        int i = 0, j = str.length() - 1;
        while( i < str.length() && !Character.isLetter(str.charAt(i))) {
            i++;
        }
        while( j >= 0 && !Character.isLetter(str.charAt(j))) {
            j--;
        }
        // 알파벳이 존재하는 문자열의 시작 위치(i), 끝 위치(j)를 얻게됨
        if(i > j) // substring에서 오류가 발생하므로
            return null
        else
            return str.substring(i, j + 1); // substring은 [  )
    }
    
    
    // 어떤 문자열이 목록에 이미 있는지 없는지 확인하고 -> 이미 있으면 제외 없으면 추가
    static void addWord(String str) {
        int index = findWord(str); // returns -1 if not found
        if (index != -1) { // found
            count[index]++;
        }
        else { // not found
            int i = n-1;
            while(i >=0 && words[i].compareTo(str) > 0) {
                words[i+1= words[i];
                count[i+1= count[i];
                i--;
            }
            words[i+1= str; // 배열 내 값이 null인 것들(그 전까지는 문자열들로 채워져있고) 중 첫 번째에 문자열을 저장
            count[i+1= 1// 새로운 문자열을 추가하면서 동시에 등장 횟수 1로 지정
            n++// 목록에 저장된 단어의 개수 +1
        }
    }
    
    // words 배열에 어떤 문자열이 존재하지는지, 존재한다면 그 위치(index)를 반환
    static int findWord(String str) {
        for (int i = 0; i < n; i++) {
            if(words[i].equalsIgnoreCase(str)) { // 대소문자 구분하지 않고 동일한지 비교
                return i; // 존재한다면 그 위치를 반환
            }
        }
        return -1// 존재하지 않는다면 -1을 반환
    }
    
    // 파일로 저장(출력 = 파일 쓰기)
    static void saveAs(String filename){
        try {
            PrintWriter pw = new PrintWriter(new FileWriter(filename));
            for (int i = 0; i < n; i++) {
                pw.println(words[i] + " " + count[i]);
            }
            pw.close();
        } catch (IOException e) {
            System.out.println("파일 저장 실패");
            return;
        }
    }
    
 
}
 
cs

알파벳(소문자) 단어들만 존재하고, 사전식 순서로 정렬된 모습을 확인할 수 있다.

 

강의 내용 및 자료 출처: 권오흠 교수님

 

Theme. 함수

1. return 값이 있는 함수

 

if(k<2) { return false } 부분은 1이 소수로 포함되지 않도록 하는 부분!

 

2. return 값이 없는 함수

 

사용자로부터 n개의 정수를 입력받은 후 오름차순으로 정렬(sort)하여 출력해라.

Bubble sort를 이용한 함수를 생성하여 코드를 작성해본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package CH1;
 
import java.util.Scanner;
 
public class Code18 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] data = new int[n];
        for (int i = 0; i < n; i++) {
            data[i] = sc.nextInt();
        }
        sc.close();
        
        bubblesort(n, data);
        System.out.print("Sorted data: ");
        for (int i = 0; i < n; i++) {
            System.out.print(data[i] + " ");
        }
        
    }
    
    static void bubblesort(int n, int[] data) {
        for (int i = n-1; i > 0; i--) {
            for (int j = 0; j < i; j++) {
                if(data[j] > data[j+1]) {
                    int tmp = data[j];
                    data[j] = data[j+1];
                    data[j+1= tmp;
                }
            }
        }
        return;
    }
}
 
cs

 

 이때, swap하는 과정을 함수로 나타내어 호출하고자 아래와 같은 코드를 작성하면,

실행시켜보면, 정렬이 되지 않는다. 무엇이 문제일까?

"값에 의한 호출"에 대해 이해하면 그 이유를 알 수 있다.

매개변수의 전달과정에서

호출문의 data[j], data[j+1]은 각각 data 배열에 존재하는 값(배열의 한 칸에 해당)으로 actual parameter이다.

호출된 메서드의 a, b는 formal parameter이다.

"값에 의한 호출"이란 결론적으로 말하면 actual parameter와 formal parameter는 별개의 변수라는 것(메모리 영역에서 서로 다른 영역을 차지한다)이다.

다만, 메서드를 호출하는 순간(호출문이 실행되는 순간) actual parameter 값이 formal parameter에 복사가 된다.

즉, 값만 복사가 되는 것이지 서로 별개의 변수이므로 a,b를 스왑한다고 해서 data[j], data[j+1]이 스왑되는 것은 아니다. 

복사본들끼리 스왑한다고 원본들이 스왑되는 것은 아니다라고 이해해도 좋다.

 

그런데!!

swap메서드를 추가하지 않고, bubblesort 메서드만을 이용하여 코드를 작성했을 때에도,

호출문: bubblesort(n, data);

호출된 메서드: static void bubblesort(int n, int[] data); ...에서

"값에 의한 호출"에 의해 정렬이 올바르게 되지 않았어야 하는 것이 아닌가? 라는 의문이 들 수 있다.

하지만, 정렬은 올바르게 된다. 

무슨 차이점이 있는가? 

 

답은 "배열"에 있다. 정확히 말하자면, 배열만이 예외는 아니다.

즉, 기본형(primitive type)의 매개변수(8가지)는 호출된 메서드에서 값을 변경하더라도 호출한 쪽에 영향을 주지 못한다. 이것은 "값에 의한 호출"이기 때문이다.

하지만, 배열은 참조형 변수다. 따라서, "값에 의한 호출"에 영향을 받지 않는다.

배열의 값은 호출된 메서드에서 변경하면 호출한 쪽에서도 변경된다.

 

txt파일에 몇 명의 사람들이 존재하는지 모르므로 배열의 길이를 1000으로 둔 것.

하지만, 위와 같이 코드를 작성하면 FileNotFoundException이 발생한다. try catch문을 작성하도록 한다.

위 코드에서의 return은 main 메서드를 종료하게 되는데, 이는 곳 프로그램 종료를 의미하므로 System.exit(0)으로 대체할 수 있다.

여러 개의 클래스에 사용되는 변수가 존재하므로, 그 변수들을 멤버변수로 선언한다.

위 코드의 name[j] > name[j+1] 부분에서 에러가 발생한다.

각각 String의 값이기 때문인데,

문자열 간의 사전식 순서를 검사하기 위한 compareTo메서드는 1,0,-1 3가지 값을 반환하게 된다.

즉, name[j].compareTo(name[j+1]) < 0 이면, name[j]가 name[j+1]보다 사전식 순서가 앞에 위치하는 것이고(순서가 더 빠르다)

name[j].compareTo(name[j+1]) = 0 이면, name[j]와 name[j+1]의 사전식 순서가 동일하다는 것이며

name[j].compareTo(name[j+1]) > 0 이면, name[j]가 name[j+1]보다 사전식 순서가 뒤에 위치하는 것(순서가 더 느리다)이다.

여기서는 쓰이지 않았지만, 두 문자열 str1, str2에 대해서 두 문자열이 동일한지, 동일하면 true(1), 동일하지 않다면 false(0)를 반환하는 메서드는 equals다. 즉, str1.equals(str2)와 같이 사용하면 된다.

 

2가지 방법으로 풀 수 있는데,

먼저, 첫 번째 방법의 코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package CH1;
 
import java.util.Scanner;
 
public class Code21 {
    static int n;
    static int[][] grid;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        grid = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                grid[i][j] = sc.nextInt();
            }
        }
        for (int x = 0; x < n; x++) {
            for (int y = 0; y < n; y++) {
                for (int dir = 0; dir < 8; dir++) {
                    for (int len = 1; len <= n; len++) {
                        int val = computeValue(x,y,dir,len);
                        if(val != -1 && isPrime(val)) {
                            System.out.println(val);
                        }
                    }
                }
            }
        }
        
    }
    public static int computeValue(int x, int y, int dir, int len) {
        int val = 0;
        for (int i = 0; i < len; i++) {
            int digit = getDigit(x,y,dir,i);
            if(digit == -1) {
                return -1;
            }
            val = val * 10 + digit;
        }
        return val;
    }
    
    public static int getDigit(int x, int y, int dir, int k) {
        int newX = x;
        int newY = y;
        switch(dir) {
            case 0: newY -= k; break;
            case 1: newX += k; newY -= k; break;
            case 2: newX += k; break;
            case 3: newX += k; newY += k; break;
            case 4: newY += k; break;
            case 5: newX -= k; newY += k; break;
            case 6: newX -= k; break;
            case 7: newX -= k; newY -= k; break;
        }
        if(newX < 0 || newX >= n || newY < 0 || newY >= n) {
            return -1;
        }
        return grid[newX][newY];
    }
    
    public static boolean isPrime(int val) {
        boolean b = true;
        if(val < 2) {
            b = false;
        }
        for (int i = 2; i*<= val ; i++) {
            if(val % i == 0) {
                b = false;
                break;
            }
        }
        return b;
    }
    
}
 
cs

 

val = val * 10 + digit 부분에 해당

i칸 떨어진 자리에 있는 digit를 반환하는 것이기 때문에, i는 i == 0(즉, 한자리 수)부터 i == (len-1)(즉, len자리 수)까지 증가할 수 있다.

 이때, x와 y의 양의 방향을 각각 오른쪽 방향, 아래쪽 방향으로 잡았다.

 

두번째 방법의 코드에서는 아래와 같이 좌표의 증감분에 대한 배열들을 미리 만들어 놓아서 첫번째 코드의 switch문을 대신할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package CH1;
 
import java.util.Scanner;
 
public class Code21 {
    static int n;
    static int[][] grid;
    static int[] offsetX = {0,1,1,1,0,-1,-1,-1};
    static int[] offsetY = {-1,-1,0,1,1,1,0,-1};
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        grid = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                grid[i][j] = sc.nextInt();
            }
        }
        for (int x = 0; x < n; x++) {
            for (int y = 0; y < n; y++) {
                for (int dir = 0; dir < 8; dir++) {
                    for (int len = 1; len <= n; len++) {
                        int val = computeValue(x,y,dir,len);
                        if(val != -1 && isPrime(val)) {
                            System.out.println(val);
                        }
                    }
                }
            }
        }
        
    }
    public static int computeValue(int x, int y, int dir, int len) {
        int val = 0;
        for (int i = 0; i < len; i++) {
            int digit = getDigit(x,y,dir,i);
            if(digit == -1) {
                return -1;
            }
            val = val * 10 + digit;
        }
        return val;
    }
    
    public static int getDigit(int x, int y, int dir, int k) {
        int newX = x + k*offsetX[dir];
        int newY = y + k*offsetY[dir];
        
        if(newX < 0 || newX >= n || newY < 0 || newY >= n) {
            return -1;
        }
        else {
            return grid[newX][newY];
        }
    }
    
    public static boolean isPrime(int val) {
        boolean b = true;
        if(val < 2) {
            b = false;
        }
        for (int i = 2; i*<= val ; i++) {
            if(val % i == 0) {
                b = false;
                break;
            }
        }
        return b;
    }
    
}
 
cs

 

메서드 끝.

강의 내용 및 자료 출처: Java로 배우는 자료구조(권오흠 교수님)

 

Theme. 변수

 

Java 프로그램은 하나 혹은 그 이상의 클래스로 구성된다. 

즉, Java는 class의 집합이고, class는 method의 집합이다.

main method는 프로그램 실행이 시작되는 곳이다.

 

변수는 "적용 범위(scope)"를 가진다.

메서드(블록) 내부에 선언된 변수는 그 메서드(블록) 내부에서만 사용 가능하고, 

메서드 외부에(클래스 내부) 선언된 변수는 그 클래스 내에서 사용가능하다.

 

"==" 연산자를 통한 동일성 비교는 기본형끼리는 가능하지만, 아래와 같이 String 간의 비교 등에서는 불가능하다.

String 간의 비교는 equals()를 사용한다.

 

 

PS(Problem Solving)을 하는 중에 항상 입력을 Scanner로 하다가 시간 초과를 만났다.

그동안 입력은 Scanner, 출력은 System.out.println()을 고정적으로 사용해왔는데, 이와 비교했을 때 입출력 효율이 훨씬 좋은 BufferedReader/BufferedWriter을 이용하여 입/출력을 하는 방법에 대해 정리해보도록 한다.

 

먼저, 두 함수에 공통적으로 적혀있는 "Buffer"는 무엇일까?

버퍼(buffer)는 데이터를 전송할 때, 그 과정에서 일시적으로 데이터를 보관하는 임시 메모리 영역이다.

 

Scanner와 같이 버퍼를 이용하지 않는 입력의 경우 키보드의 입력이 있을 때 즉시 키보드에서 프로그램으로 데이터가 전송되고,

BufferedReader와 같이 버퍼를 이용하는 입력의 경우 키보드의 입력이 있을 때마다 한 문자씩 버퍼에 전송되고, 버퍼가 가득 차거나 개행 문자("\n"과 같은)가 나타나면 버퍼에 있던 내용을 한번에 프로그램에 전송한다. 

 

결론적으로, 버퍼를 사용한 입출력이 그렇지 않은 입출력보다 속도가 빠른 것을 기억하고, 어떻게 사용하는 지를 정리해보자. 

백준 문제 번호 15552번 "빠른 A+B"를 통해 설명하도록 한다. (맨 아래 풀이가 있다)

https://www.acmicpc.net/problem/15552

 

15552번: 빠른 A+B

첫 줄에 테스트케이스의 개수 T가 주어진다. T는 최대 1,000,000이다. 다음 T줄에는 각각 두 정수 A와 B가 주어진다. A와 B는 1 이상, 1,000 이하이다.

www.acmicpc.net

Theme. 입력! BufferedReader vs Scanner

BufferedReader를 이용한 입력을 설명하기에 앞서 주로 사용하던 Scanner를 사용하여 위 문제의 입력 부분 중 일부인 A=1, B=1을 입력받기 위해 코드를 작성해보면,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.Scanner// Scanner 클래스 호출
 
public class Main {
    public static void main(String[] args){
        // Scanner 객체 생성
        Scanner sc = new Scanner(System.in); 
        
        // 정수 A,B 입력 및 리턴 
        int A = sc.nextInt();
        int B = sc.nextInt();
 
        System.out.println(A); // 1
        System.out.println(B); // 1
        
    }
}
cs

와 같이 작성할 수 있다.

잠시 Scanner에 대해 정리해보자.

Scanner의 입력 메소드들은 공백(띄어쓰기) 또는 개행("\n" 등)을 기준으로 읽는다. 

즉 위 예제에서 "1 1"을  콘솔에 입력했을 때, 1과 다음 1 사이에 공백(띄어쓰기)가 존재하므로 A에 1이 입력되고,

두 번째 1을 입력 후 enter를 쳤을 때(개행) B에 1이 입력되게 된다. 

위 문제에서는 정수형 변수 A, B에 값을 입력받았으므로 Scanner의 입력 메소드 중 nextInt()를 사용했지만,
만약 String의 값을 입력받고자 한다면, next() 또는 nextLine()을 사용할 수 있다.
1
2
3
4
int n = nextInt(); 
double d = nextDouble();
String s1 = next(); // 공백(띄어쓰기)를 기준으로 한 단어씩 읽음
String s2 = nextLine(); // 개행을 기준으로 한 줄씩 읽음
cs

이때, 한 가지 주의할 점은 char의 값을 바로 입력받는 메소드는 없다(nextChar() 이런 메소드는 없다)

next()와 nextLine()의 차이를 간단한 예를 통해 확인해보자.

next()

next()공백을 기준으로 한 단어씩 읽다보니, "완벽하지 않은 것들에 대한 사랑"이라는 문장에서 "완벽하지"까지만 읽고 이를 변수 str에 저장했음을 알 수 있다.

next()를 이용해서 위 문장을 전부 읽기 위해서는 다음과 같이 코드를 작성할 수 있을 것이다.

하지만, 이와 같은 방법은 입력하는 문자열의 공백이 몇 개인지를 파악해야 한다는 불편함이 존재한다.

이에 개행을 기준으로 한 줄씩 읽는 nextLine()메소드를 사용하면, 

한 줄 전체가 str에 입력된 것을 확인할 수 있다.

 

이제 BufferedReader를 이용한 입력 방법에 대해서 정리해보자.

 

BufferedReader의 가장 큰 특징은 입력받은 데이터가 String으로 고정되어 있다는 것이다. 그래서 다른 type으로 입력을 받기 위해서는 형변환을 해야 하고, 이때 예외처리를 반드시 해줘야 한다.

예외처리를 위해 main 메소드에 throws IOException를 추가하는 것이 일반적이다. 

 

또한, BufferedReader는 한 줄씩(개행 기준) 읽게 된다.

 

또한, 입력은 readLine() 메소드를 사용한다.

이를 코드로 작성해보면,

1
2
3
4
5
6
7
8
9
10
11
12
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();
        int i = Integer.parseInt(br.readLine());
 
    }
}
cs

 

읽은 데이터들은 한 줄 단위이기 때문에 Scanner에서 공백을 기준으로 한 단어씩 읽는 경우와 동일한 결과를 가져오기 위해서 추가적인 작업이 필요하다. 

이때 사용하는 것이 StringTokenizer 또는 String.split()이다.

StringTokenizer의 nextToken() 함수를 통해 공백을 기준으로 한 단어씩 읽을 수 있게 해주거나,

String.split()을 통해 문자열 배열을 만들고, 인덱스별 각 원소에 한 단어씩 저장하는 것이다.

이를 코드로 작성해보면,

1. StringTokenizer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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 = Integer.parseInt(st.nextToken());
        int B = Integer.parseInt(st.nextToken());
        
        // 콘솔에 5 4라고 입력하면?
        
        System.out.println(A); // 5
        System.out.println(B); // 4
        
    }
}
cs

2. String.split()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();
        String[] arrStr = str.split(" ");
 
        // 콘솔에 5 4라고 입력하면?
        System.out.println(arrStr[0]); // 5
        System.out.println(arrStr[1]); // 4
    }
}
cs

 

Theme. 출력! BufferedWriter vs System.out.println()

먼저, 간단한 예시를 살펴보자.

일반적인 출력문인 System.out.println()은 데이터의 양이 적을 때의 출력에는 효율성을 따질 필요가 없이 유용하게 사용할 수 있다. 하지만, 출력해야 할 데이터의 양이 많아졌을 때는 버퍼를 사용한 출력을 하는 것이 효율적이다.

버퍼를 사용하는 출력이 BufferedWriter이다.

위 코드에서 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); 부분이 출력을 위해 객체를 생성한 부분이다. 

br.readLine()을 통해 입력 받은 문자열을 StringTokenizer와 nextToken()을 통해 각각 str1, str2라는 변수에 저장했다.

str1과 str2에 저장된 데이터를 출력하기 위해서 bw.write(str1), bw.write(str2)를 작성한 것이다.

즉, write()를 통해 출력을 하는 것이다. 

System.out.println()에서는 개행의 효과가 있지만, write()에는 개행의 효과가 없다.(마치, System.out.print()와 같은 효과)

따라서, newLine()을 통해 개행을 해줄 수 있다.

write()를 통해 출력을 다 하고 나면, flush(), close()를 통해 출력 스트림을 닫도록 한다.

 

이제, 백준 15552번: 빠른 A+B를 푼 코드를 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;
 
public class Main {
    public static void main(String[] args) throws IOException {
 
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        
        int T = Integer.parseInt(br.readLine()); // 테스트케이스의 개수 T 입력받음
        //출력할 때, 한 줄씩 합을 구하고 개행해줌
        for (int i = 0; i < T; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine()); // 1 1 입력 받고 A, B에 각각 저장하기 위함
            bw.write(Integer.parseInt(st.nextToken()) + Integer.parseInt(st.nextToken()) + "\n");
        }
        bw.flush();
        bw.close();
 
    }
}
cs

결과는

 

'Java' 카테고리의 다른 글

Singleton 간단하게 구현해보기  (0) 2023.02.09
HashMap 간단한 사용법  (0) 2023.02.08
String - Java  (0) 2023.01.15
참조변수 super, 생성자 super()  (0) 2022.12.31
오버라이딩(overriding)  (2) 2022.12.30

자바에서는 Data Type(값의 타입)은 8가지이다. 이를 기본형이라고 하는데, 

1. 문자 - char

2. 숫자 

 2-1. 정수 - byte, short, int, long

 2-2. 실수 - float, double

3. 논리 - boolean

으로 구성된다.

 

변수(Variable)의 타입은 기본형과 참조형으로 나눌 수 있다.

1.기본형(Primitive type)

 위의 8가지와 동일하고, 실제 값을 저장한다.

2. 참조형(Reference type)

 기본형을 제외한 나머지(String, System 등)를 의미한다.

 메모리 주소를 저장한다. ( 4byte 또는 8byte )

 객체의 주소를 저장한다.

 

Theme. 기본형의 종류와 크기

1. 논리형

 boolean type으로 true와 false 중 하나를 값으로 갖으며, 조건식과 논리적 계산에 사용된다.

 

2. 문자형

 char type으로 문자를 저장하는데 사용되며, 변수 당 하나의 문자만을 저장할 수 있다.

 

3. 정수형

 byte, short, int, long type으로 정수 값을 저장하는 데 사용된다.

 

4. 실수

 float, double type으로 실수 값을 저장하는 데 사용된다.

 

기본형의 종류에 따른 크기를 나타낸 표는 다음과 같다.

종류 / 크기(byte) 1 2 4 8
논리형 boolean      
문자형   char    
정수형 byte short int long
실수형     float double

 

Theme. 기본형의 표현 범위

1. byte

byte b;

1byte = 8bit이고, 1bit는 2진수의 한자리를 의미한다(0 or 1이 존재하는 자리)

1bit당 0 or 1의 2개의 값을 표현할 수 있으므로 n비트로 표현할 수 있는 값의 개수는 2^n개이다.

n비트로 표현할 수 있는 부호없는 정수(양수)의 범위: 0 ~ 2^n - 1 (0을 포함해야 해서 2^n에서 1을 빼줌)

따라서 , 8비트로 표현할 수 있는 부호 없는 정수(양수)의 범위는 0 ~ 255이다.

n비트로 표현할 수 있는 부호있는 정수의 범위: -2^(n-1) ~ 2^(n-2) - 1

따라서, 8비트로 표현할 수 있는 부호 없는 정수의 범위는 -128 ~ 127이다.

 

byte type의 변수에 값이 저장되는 방식은 아래 그림과 같다.

먼저, 전체 8bit 중 왼쪽의 1bit는 값의 부호를 나타내는 데 사용하는데, 이 비트를 .부호 비트(Sign bit)라고 한다.

부호비트의 값이 0이면 양수이고, 1이면 음수이다.

7bit는 128개의 값을 표현할 수 있으므로 양수 128개(0 ~ 127), 음수 128개(-128 ~ -1) 총 256개의 수를 나타낼 수 있다.

 

2. short

 short는 크기가 2byte이므로 총 16bit이다.

위와 같은 방법으로 계산해보면,

2^15 = 32768이므로 -32768 ~ 32767의 수를 표현할 수 있다.

 

3. char

 char는 크기가 2byte이므로 총 16bit이다. 

4. int

 int는 크기가 4byte이므로 총 32bit이다.

5. float, double

E38 = 10^38을 의미.

이때, float와 int는 동일한 4byte의 크기를 가지는데, float의 저장 가능한 값의 범위가 훨씬 큰 이유는 무엇일까?

그 이유는 값의 저장 방식에 있다.

S는 부호, E는 지수, M은 가수를 의미하는데, 3.4E38에서 3.4가 가수이고, 38이 지수에 해당한다. 따라서 int보다 더 큰 범위를 가지게 되는 것이다.

위 표에서 "정밀도"란 값과의 오차없이 표현할 수 있는 자릿수를 의미한다.

float의 경우 10진수로 7자리까지는 오차없이 표현할 수 있고, double의 경우 15자리까지는 오차없이 표현할 수 있다.

'Java > 자바의 정석 정리' 카테고리의 다른 글

문자와 문자열  (0) 2023.01.16
변수, 상수, 리터럴  (2) 2023.01.04
변수(variable)  (0) 2023.01.03

+ Recent posts