package com.eomcs.mylist;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
// 이 클래스가 클라이언트 요청 처리 담당자임을 표시한다.
// 이 표시(애노테이션)가 붙어 있어야만 스프링부트가 이 클래스를 인식한다.
public class ContactController {
String[] contacts = new String[5];
int size = 0;
@RequestMapping("/contact/list")
public Object list() {
String[] arr = new String[size]; // 배열에 저장된 값만 복사할 새 배열을 만든다.
for (int i = 0; i < size; i++) {
arr[i] = contacts[i]; // 전체 배열에서 값이 들어 있는 항목만 복사한다.
}
return arr; // 복사한 항목들을 담고 있는 새 배열을 리턴한다.
}
@RequestMapping("/contact/add")
public Object add(String name, String email, String tel, String company) {
String contact = name + "," + email + "," + tel + "," + company;
contacts[size++] = contact;
return size;
}
@RequestMapping("/contact/get")
public Object get(String email) {
for (int i = 0; i < size; i++) {
if (contacts[i].split(",")[1].equals(email)) { // 예) "u1@test.com"
return contacts[i];
}
}
return "";
}
@RequestMapping("/contact/update")
public Object update(String name, String email, String tel, String company) {
String contact = name + "," + email + "," + tel + "," + company;
for (int i = 0; i < size; i++) {
if (contacts[i].split(",")[1].equals(email)) {
contacts[i] = contact;
return 1;
}
}
return 0;
}
@RequestMapping("/contact/delete")
public Object delete(String email) {
for (int i = 0; i < size; i++) {
if (contacts[i].split(",")[1].equals(email)) { // 예) "u1@test.com"
// 현재 위치의 다음 항목에서 배열 끝까지 반복하며 앞으로 값을 당겨온다.
for (int j = i + 1; j < size; j++) {
contacts[j - 1] = contacts[j];
}
size--;
return 1;
}
}
return 0;
}
}
다음의 코드를 리팩토링할때 다음의 과정을 거친다.
1) 한 사람의 연락처 정보를 문자열로 만드는 코드를 메서드로 분리한다.
String createCSV(String name, String email, String tel, String company) {
return name + "," + email + "," + tel + "," + company;
}
@RequestMapping("/contact/add")
public Object add(String name, String email, String tel, String company) {
contacts[size++] = createCSV(name, email, tel, company);
return size;
}
@RequestMapping("/contact/update")
public Object update(String name, String email, String tel, String company) {
for (int i = 0; i < size; i++) {
if (contacts[i].split(",")[1].equals(email)) {
contacts[i] = createCSV(name, email, tel, company);
return 1;
}
}
return 0;
}
2) 이메일로 연락처를 찾아 배열 인덱스를 알아내는 코드를 분리한다.
int indexOf(String email) {
for (int i = 0; i < size; i++) {
if (contacts[i].split(",")[1].equals(email)) {
return i;
}
}
return -1;
}
@RequestMapping("/contact/get")
public Object get(String email) {
int index = indexOf(email);
if (index == -1) {
return "";
}
return contacts[index];
}
public Object update(String name, String email, String tel, String company) {
int index = indexOf(email);
if (index == -1) {
return 0;
}
contacts[index] = createCSV(name, email, tel, company);
return 1;
}
3) 배열 항목 삭제 코드를 분리한다.
코드 기능을 명확하게 설명하고 싶을 때도 메서드를 활용하여 코드를 분리한다.
String remove(int index) {
String old = contacts[index];
for (int i = index + 1; i < size; i++) {
contacts[i - 1] = contacts[i];
}
size--;
return old;
}
@RequestMapping("/contact/delete")
public Object delete(String email) {
int index = indexOf(email);
if (index == -1) {
return 0;
}
remove(index);
return 1;
}
4) 배열 크기를 자동으로 늘린다.
@RequestMapping("/contact/add")
public Object add(String name, String email, String tel, String company) {
if (size == contacts.length) { // 배열이 꽉찼다면,
// 기존 배열 보다 50% 큰 배열을 새로 만든다.
int newCapacity = contacts.length + (contacts.length >> 1);
String[] arr = new String[newCapacity];
// 기존 배열의 값을 새 배열로 복사한다.
for (int i = 0; i < contacts.length; i++) {
arr[i] = contacts[i];
}
// 기존 배열 대신 새 배열을 연락처 저장 배열로 사용한다.
contacts = arr;
}
contacts[size++] = createCSV(name, email, tel, company);
return size;
}
5) 배열 크기를 늘리는 코드를 별도의 메서드로 분리한다.
코드 기능을 명확하게 설명하고 싶을 때도 메서드를 활용하여 코드를 분리한다.
String[] grow() {
// 기존 배열 보다 50% 큰 배열을 새로 만든다.
int newCapacity = contacts.length + (contacts.length >> 1);
String[] arr = new String[newCapacity];
// 기존 배열의 값을 새 배열로 복사한다.
for (int i = 0; i < contacts.length; i++) {
arr[i] = contacts[i];
}
return arr;
}
@RequestMapping("/contact/add")
public Object add(String name, String email, String tel, String company) {
if (size == contacts.length) { // 배열이 꽉찼다면,
contacts = grow(); // 메서드 이름에서 해당 코드에 대한 설명을 짐작할 수 있다.
}
contacts[size++] = createCSV(name, email, tel, company);
return size;
}
6) 배열 크기를 계산하는 코드를 별도의 메서드로 분리한다.
코드 기능을 명확하게 설명하고 싶을 때도 메서드를 활용하여 코드를 분리한다.
// 기능:
// - 주어진 배열에 대해 50% 증가시킨 새 배열의 길이를 알려준다.
//
int newLength() {
return contacts.length + (contacts.length >> 1);
}
String[] grow() {
String[] arr = new String[newLength()];
// 기존 배열의 값을 새 배열로 복사한다.
for (int i = 0; i < contacts.length; i++) {
arr[i] = contacts[i];
}
return arr;
}
7) 배열을 복사하는 코드를 메서드로 분리한다.
코드 기능을 명확하게 설명하고 싶을 때도 메서드를 활용하여 코드를 분리한다.
// 기능:
// - 배열을 복사한다.
//
void copy(String[] source, String[] target) {
// 개발자가 잘못 사용할 것을 대비해서 다음 코드를 추가한다.
// 즉 target 배열이 source 배열 보다 작을 경우 target 배열 크기만큼만 복사한다.
int length = source.length;
if (target.length < source.length) {
length = target.length;
}
for (int i = 0; i < length; i++) {
target[i] = source[i];
}
}
String[] grow() {
String[] arr = new String[newLength()];
copy(contacts, arr);
return arr;
}
8) 완성된 코드
package com.eomcs.mylist;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ContactController {
String[] contacts = new String[5];
int size = 0;
@RequestMapping("/contact/list")
public Object list() {
String[] arr = new String[size];
for (int i = 0; i < size; i++) {
arr[i] = contacts[i];
}
return arr;
}
@RequestMapping("/contact/add")
public Object add(String name, String email, String tel, String company) {
if (size == contacts.length) { // 배열이 꽉찼다면,
contacts = grow(); // 메서드 이름에서 해당 코드에 대한 설명을 짐작할 수 있다.
}
contacts[size++] = createCSV(name, email, tel, company);
return size;
}
@RequestMapping("/contact/get")
public Object get(String email) {
int index = indexOf(email);
if (index == -1) {
return "";
}
return contacts[index];
}
@RequestMapping("/contact/update")
public Object update(String name, String email, String tel, String company) {
int index = indexOf(email);
if (index == -1) {
return 0;
}
contacts[index] = createCSV(name, email, tel, company);
return 1;
}
@RequestMapping("/contact/delete")
public Object delete(String email) {
int index = indexOf(email);
if (index == -1) {
return 0;
}
remove(index); // 메서드 이름으로 코드의 의미를 짐작할 수 있다. 이것이 메서드로 분리하는 이유이다.
return 1;
}
// 기능:
// - 입력 받은 파라미터 값을 가지고 CSV 형식으로 문자열을 만들어 준다.
//
String createCSV(String name, String email, String tel, String company) {
return name + "," + email + "," + tel + "," + company;
}
// 기능:
// - 이메일로 연락처 정보를 찾는다.
// - 찾은 연락처의 배열 인덱스를 리턴한다.
//
int indexOf(String email) {
for (int i = 0; i < size; i++) {
if (contacts[i].split(",")[1].equals(email)) {
return i;
}
}
return -1;
}
// 기능:
// - 배열에서 지정한 항목을 삭제한다.
//
String remove(int index) {
String old = contacts[index];
for (int i = index + 1; i < size; i++) {
contacts[i - 1] = contacts[i];
}
size--;
return old;
}
// 기능:
// - 배열의 크기를 늘린다.
// - 기존 배열의 값을 복사해온다.
//
String[] grow() {
String[] arr = new String[newLength()];
copy(contacts, arr);
return arr;
}
// 기능:
// - 주어진 배열에 대해 50% 증가시킨 새 배열의 길이를 알려준다.
//
int newLength() {
return contacts.length + (contacts.length >> 1);
}
// 기능:
// - 배열을 복사한다.
//
void copy(String[] source, String[] target) {
// 개발자가 잘못 사용할 것을 대비해서 다음 코드를 추가한다.
// 즉 target 배열이 source 배열 보다 작을 경우 target 배열 크기만큼만 복사한다.
int length = source.length;
if (target.length < source.length) {
length = target.length;
}
for (int i = 0; i < length; i++) {
target[i] = source[i];
}
}
}
9) 추가예시
Exam0110.java ~ Exam0130.java 참고
1) 개념 및 기본 문법
Exam0210.java ~ Exam0240.java 참고
2) 가변 파라미터
Exam0250.java ~ Exam0271.java 참고
3) 메서드 중첩 호출
Exam0280.java 참고
4) 메서드에서의 call by value
Exam0310.java 참고
4) 메서드에서의 call by reference
Exam0320.java ~ Exam0340.java 참고
5) JVM 메모리
Exam0410.java 참고
JVM과 메모리 영역
Exam0410 에서 클래스 실행 과정과 메모리 영역
6) Heap 메모리 영역
Exam0420.java, 0421.java 참고
JVM과 메모리 영역
Exam0420 에서 클래스 실행 과정과 메모리 영역