Java Day20

YDC·2025년 7월 10일

우테코8기

목록 보기
20/23

Day19 복습

char → int 변환 (char - ‘0’)

Arrays.fill 이란?

array를 채워주는 문법

버블정렬

버블정렬은 첫번재와 두번를 비교함 왼쪽이 오른쪽보다 크면 교환(swap)함
그리고 두번째와 3번째를 비교함 이걸 계속 반복함 그걸 패스쓰루라고함 더이상 반복하지 않으면 정렬이 끝남

버블 정렬의 효율성

버블 정렬은 원소 N개가 있을때 N^2번의 단계가 필요하고
빅오 표기법은 버블 정렬의 효율성을 O(N^2)라고 하고 이를 이차 시간이라고도 부름

해시란?

Key Value 형태로 데이터를 저장하는 자료구조
빠르게 찾고 싶을때 사용함 O(1)중복 key 불가능 함
아이디 찾기 , 캐시, 카운팅에 주로 사용됨

트리란?

계층적 구조의 노드기반 자료구조
루트 - 자식 노드 형태로 뻗어감 중복 안됨
BST기준 O(logN)
정렬된 자료보관, 탐색 트리 에 사용됨

HTTP란?

HyperTextTransferProtocol
클라이언트(브라우저,앱)와 서버가 서로 데이터를 주고받기 위한 약속된 통신 규칙

http 통신 흐름

클라이언트 -> 요청(Request) -> 서버
서버 -> 응답(Response) -> 클라이언트

http 요청 구조 (Request)

GET /todos HTTP/1.1 // 요청라인 : 메서드 + 경로 + 버전
Host: example.com
User-Agent: Chrome/...
Content-Type: application/json // 헤더 : 부가정보
(빈 줄) // 본문과 헤더 구분
{"task":"청소하기","done":false} 바디 :실제데이터

http 응답 구조(Response)

HTTP/1.1 200 OK // 상태라인 : 버전+상태 코드 + 메세지
Content-Type: application/json //헤더 부가 정보
Content-Length: 37
(빈 줄) // 본문과 헤더 구분
{"id":1,"task":"청소하기","done":false} //바디 :응답 데이터

Http 메소드

GET 데이터 조회
POST 새 데이터 생성
PUT 데이터 전체 수정
PATCH 데이터 일부 수정
DELETE 삭제

상태코드

200 OK 성공 요청 정상 처리
201 Created 생성됨 POST 성공 시
400 Bad Request 잘못된 요청 파라미터 오류
401 Unauthorized 인증 실패 로그인 필요
403 Forbidden 접근 금지 권한 없음
404 Not Found 없는 자원 잘못된 URL
500 Internal Server Error 서버 에러 코드 오류 발생 시

RestAPI란?

REpresentational State TransferApplication Programming Interface
HTTP 기반으로 동작하는
규칙이 정해진 방식의 URL과 메서드 설계
클라이언트가 자원(Resource) 에 요청(Request) 을 보내고, 서버가 응답(Response) 하는 구조

Rest ApI의 3요소

자원 : 데이터의 대상 (/todos,users/1)
행위 : 어떤 동작인지 (GET,POST,PUT,DELETE)
표현 : 주고받는 데이터 형식 ( Json,Xml등)

Restful API의 핵심 규칙

uri는 자원을 표현함 /todos,/todos/1 동사는 안씀 /getTodo,/addTodo
HTTP 메소드로 행위를 구분함 GET,POST,PUT,DELETE
요청 간 서버가 상태 저장을 하지 않음
일관된 구조를 사용함

학습목표

백준

  • 1152 단어의 개수
  • 2908 상수
  • 5622 다이얼

CS

  • 누구나 자료구조와 알고리즘 5강
  • HTTPS

JAVA

  • 저장소 추상화(Refactor)

1.백준

1.1152 단어의 개수

문제

영어 대소문자와 공백으로 이루어진 문자열이 주어진다. 이 문자열에는 몇 개의 단어가 있을까? 이를 구하는 프로그램을 작성하시오. 단, 한 단어가 여러 번 등장하면 등장한 횟수만큼 모두 세어야 한다.

입력

첫 줄에 영어 대소문자와 공백으로 이루어진 문자열이 주어진다. 이 문자열의 길이는 1,000,000을 넘지 않는다. 단어는 공백 한 개로 구분되며, 공백이 연속해서 나오는 경우는 없다. 또한 문자열은 공백으로 시작하거나 끝날 수 있다.

출력

첫째 줄에 단어의 개수를 출력한다.

예제 입력 1

The Curious Case of Benjamin Button

예제 출력 1

6

예제 입력 2

The first character is a blank

예제 출력 2

6

예제 입력 3

The last character is a blank

예제 출력 3

6

풀이

package string;
import java.util.Scanner;

public class BOJ_1152_WordCount {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine().trim();
        if(str.isEmpty()){
            System.out.println(0);
        }else{
            String[] strArr = str.split("\\s+");
            System.out.println(strArr.length);
        }
    }
}

Trim으로 앞뒤 공백 제거 .split \s+로 여러 공백도 한번에 나눔
입력이 공백으로 시작하거나 끝나는 경우도 고려하는 문자열 전처리 실력 평가

2.2908 상수

문제

상근이의 동생 상수는 수학을 정말 못한다. 상수는 숫자를 읽는데 문제가 있다. 이렇게 수학을 못하는 상수를 위해서 상근이는 수의 크기를 비교하는 문제를 내주었다. 상근이는 세 자리 수 두 개를 칠판에 써주었다. 그 다음에 크기가 큰 수를 말해보라고 했다.
상수는 수를 다른 사람과 다르게 거꾸로 읽는다. 예를 들어, 734와 893을 칠판에 적었다면, 상수는 이 수를 437과 398로 읽는다. 따라서, 상수는 두 수중 큰 수인 437을 큰 수라고 말할 것이다.
두 수가 주어졌을 때, 상수의 대답을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 상근이가 칠판에 적은 두 수 A와 B가 주어진다. 두 수는 같지 않은 세 자리 수이며, 0이 포함되어 있지 않다.

출력

첫째 줄에 상수의 대답을 출력한다.

예제 입력 1

734 893

예제 출력 1

437

예제 입력 2

221 231

예제 출력 2

132

예제 입력 3

839 237

예제 출력 3

938

풀이

package string;
import java.util.Scanner;

public class BOJ_2908_ReverseCompare {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();
        int b = sc.nextInt();
        int c = (a/100)+((a%10)*100)+(((a/10)%10)*10);
        int d = (b/100)+((b%10)*100)+(((b/10)%10)*10);
        if(c<d){
            System.out.println(d);
        }else {
            System.out.println(c);
        }
    }
}

StringBuilder를 상용해서도 가능

import java.util.Scanner;

public class BOJ_2908_ReverseCompare {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String [] INT = sc.nextLine().split(" ");
        int a = Integer.parseInt(new StringBuilder(INT[0]).reverse().toString());
        int b = Integer.parseInt(new StringBuilder(INT[1]).reverse().toString());
        if(a > b){
            System.out.println(a);
        }else{
            System.out.println(b);
        }
    }
}

StringBuilder.reverse() 또는 수학적으로 자리수 뒤집기 능력 평가
int ↔ String 변환 능력 및 비교 로직 구현

3.5622 다이얼

문제

상근이의 할머니는 아래 그림과 같이 오래된 다이얼 전화기를 사용한다.

전화를 걸고 싶은 번호가 있다면, 숫자를 하나를 누른 다음에 금속 핀이 있는 곳 까지 시계방향으로 돌려야 한다. 숫자를 하나 누르면 다이얼이 처음 위치로 돌아가고, 다음 숫자를 누르려면 다이얼을 처음 위치에서 다시 돌려야 한다.
숫자 1을 걸려면 총 2초가 필요하다. 1보다 큰 수를 거는데 걸리는 시간은 이보다 더 걸리며, 한 칸 옆에 있는 숫자를 걸기 위해선 1초씩 더 걸린다.
상근이의 할머니는 전화 번호를 각 숫자에 해당하는 문자로 외운다. 즉, 어떤 단어를 걸 때, 각 알파벳에 해당하는 숫자를 걸면 된다. 예를 들어, UNUCIC는 868242와 같다.
할머니가 외운 단어가 주어졌을 때, 이 전화를 걸기 위해서 필요한 최소 시간을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 알파벳 대문자로 이루어진 단어가 주어진다. 단어의 길이는 2보다 크거나 같고, 15보다 작거나 같다.

출력

첫째 줄에 다이얼을 걸기 위해서 필요한 최소 시간을 출력한다.

예제 입력 1

WA

예제 출력 1

13

예제 입력 2

UNUCIC

예제 출력 2

36

풀이

package string;
import java.util.Scanner;

public class BOJ_5622_DialTimeCalc {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String dial = sc.nextLine().toUpperCase();
        int time = 0;
        for (int i = 0; i < dial.length(); i++) {
            char ch = dial.charAt(i);
            if (ch >= 'A' && ch <= 'C') {
                time += 3;
            } else if (ch >= 'D' && ch <= 'F') {
                time += 4;
            } else if (ch >= 'G' && ch <= 'I') {
                time += 5;
            } else if (ch >= 'J' && ch <= 'L') {
                time += 6;
            } else if (ch >= 'M' && ch <= 'O') {
                time += 7;
            } else if (ch >= 'P' && ch <= 'S') {
                time += 8;
            } else if (ch >= 'T' && ch <= 'V') {
                time += 9;
            } else if (ch >= 'W' && ch <= 'Z') {
                time += 10;
            }
        }
        System.out.println(time);
    }
}

조건문, 반복문, 또는 배열/맵 자료구조를 활용한 처리

toUpperCase란?

모든 영문자를 대문자로 만드는 메소드

2.CS

1.누구나 자료구조와 알고리즘 5강


선택정렬

선택정렬은 각 셀을 왼쪽부터 오른쪽 방향으로 확인하면서 어떤 값이 최솟값인지 결정함
한 셀씩 이동하면서 현재까지 가장 작은 값을 변수에 저장함 현재 변수값보다 작은 값이들어있는 셀을 만나면
변수가 새인덱스를 가르키도록 값을 대체함
처음엔 인덱스 0 이 최솟값으로 변수에 들어간 후 한번의 패스스루를 거쳐 인덱스0을 최솟값으로 만들고
인덱스1부터 다시 반복함

선택 정렬의 효율성

선택정렬은 비교와 교환 두 종류의 단계를 포함함 N개의 원소가 있을때
(N-1)+(N-2)+(N-3)…의 비교를 실행함
교환은 한 패스스루 당 최소 한 번 일어남 버블 정렬과 달리 최악의 시나리오에선 빠짐없이 교환을 한번 해야함
선택 정렬은 버블 정렬보다 단계수가 반정도 적음 즉 두배 정도 빠름

빅오는 상수를 무시함

선택 정렬은 버블 정렬의 반 즉 O(N^2/2)로 설명할 수 있지만 선 정렬은 빅오로 표현하면 똑같이 O(N^2)이다
빅 오 표기법은 지수가 아닌 수는 포함하지 않기 때문이다

빅오의 역할

빅오는 특정 시점부터 어느 유형이 다른 유형보다 속도가 빨라지는지 중요하지 않다
빅 오는 데이터가 많을때 한 알고리즘이 어떤 시점부터 빠르다는걸 보장하기 대문에
어떤 알고리즘을 서야하는지 대체로 알 수 있다

2.HTTPS

HyperTextTransferProtocol Secure
HTTP의 +보안(암호화)방식
서버와 클라이언트가 데이터를 주고받을때 누군가 훔쳐보지 못하게 암호화 해주는 통신 방식

  1. 브라우저가 서버에 접속
  2. 서버가 인증서(SSL/TLS)를 보냄
  3. 브라우저가 신뢰 가능한 인증서인지 검증
  4. 서로 암호화 키를 주고받음 (핸드셰이크)
  5. 이후부터는 모든 통신이 암호화됨

3.JAVA

1.저장소 추상화 (Refactor)

저장소 추상화란 ?
"데이터를 어디에 저장하든지, 사용하는 쪽(TodoService)은 그걸 몰라도 되게 만드는 구조"
저장방식 등을 숨기고 공통된 인터페이스만 보고 쓰게하는것

인터페이스란?

이 클래스는 이런 기능을 할것이다라고 미리 정해놓은 약속
기능은 정의하지만 구현은 나중에 알아서 함

예시)
인터페이스 선언 소리와 움직임을 정의해놓고

Interface
public interface Animal {
    void sound();
    void move();
}

나중에 구현함

Implement
public class Dog implements Animal {
    public void sound() {
        System.out.println("멍멍");
    }

    public void move() {
        System.out.println("네 발로 뛰어다님");
    }
TodoService ← TodoRepository (인터페이스 의존)
               ↳ MemoryTodoRepository
               ↳ JsonTodoRepository

사용자 요청 처리담당 TodoController Day19

import java.util.Scanner;

public class TodoController {
    private final TodoService service;

    public TodoController(TodoService service){

        this.service = service;
    }

    private int extractId(String uri) {
        try {
            String[] parts = uri.split("/");
            return Integer.parseInt(parts[2]);
        } catch (Exception e) {
            return -1;
        }
    }

public String handleRequest(String method,String url){
        if(method.equals("GET")&&url.equals("/todos")){
           return service.getAllTodos();
        } else if(method.equals("POST")&&url.equals("/todos")){
            Scanner sc = new Scanner(System.in);
            System.out.println("할일 입력");
            String task = sc.nextLine();
            System.out.print("카테고리 입력: ");
            String category = sc.nextLine();
            System.out.print("마감일 입력 (yyyy-MM-dd): ");
            String inputDate = sc.nextLine();
            return service.createTodo(task,category,inputDate);
        } else if(method.equals("GET")&&url.startsWith("/todos/")){
            int id  = extractId(url);
            return service.getTodoById(id);
        } else if(method.equals("PUT")&&url.startsWith("/todos/")){
            int id = extractId(url);
            Scanner sc = new Scanner(System.in);
            System.out.println("변경된 일 입력");
            String task = sc.nextLine();
            System.out.print("카테고리 입력: ");
            String category = sc.nextLine();
            String inputDate = sc.nextLine();
            System.out.print("마감일 입력 (yyyy-MM-dd): ");
            return service.updateTodo(id,task,category,inputDate);
        } else if(method.equals("PATCH")&&url.startsWith("/todos/")&&url.endsWith("/done")){
            int id = extractId(url);
            return service.markDone(id);
        } else if(method.equals("DELETE")&&url.startsWith("/todos/")){
            int id = extractId(url);
            return service.deleteTodo(id);
        }

        return "404 Not Found";
}
}

핵심 로직 처리 TodoService 리펙토링

사용자의 요청이 들어오면 Controller는 전부 여기로 넘겨서 처리를 함

import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Stack;
import java.util.stream.Collectors;

public class TodoService {
    private final TodoRepository repository;
    private final Stack<Todo> deleteStack = new Stack<>();

    public TodoService(TodoRepository repository) {
        this.repository = repository;
    }

    public String createTodo(String task, String category, String inputDate) {
        LocalDate dueDate;
        try {
            dueDate = (inputDate == null || inputDate.isBlank())
                    ? LocalDate.now()
                    : LocalDate.parse(inputDate);
        } catch (DateTimeParseException e) {
            dueDate = LocalDate.now();
        }
        if (category == null || category.isBlank()) {
            category = "기타";
        }
        Todo todo = new Todo(task, false, category, dueDate);
        repository.save(todo);
        return "할일이 추가 되었습니다";
    }

    public String getAllTodos() {
        List<Todo> list = repository.findAll();
        if (list.isEmpty()) {
            return "할일이 없습니다";
        }
        return list.stream()
                .map(Todo::toString)
                .collect(Collectors.joining("\n"));

    }

    public String deleteTodo(int id) {
        Todo todo = repository.findById(id);
        if (todo ==null){return"해당일이 없습니다";}
        deleteStack.push(todo);
        repository.delete(id);
        return"삭제완료";
    }

    public String updateTodo(int id, String task, String category, String inputDate) {
        Todo old = repository.findById(id);
        if (old == null) {return "해당일이 없습니다";}
        LocalDate dueDate;
        try{
            dueDate = (inputDate==null ||inputDate.isBlank())
                    ? LocalDate.now()
                    : LocalDate.parse(inputDate);
        }catch (DateTimeParseException e){
            dueDate = old.getDueDate();
        }
        Todo updated = new Todo(task, old.isDone(),category, dueDate);
        repository.update(id, updated);
        return"할일이 수정되었습니다";
    }

    public String getTodoById(int id) {
        Todo todo = repository.findById(id);
        if (todo == null) {
            return "해당 할 일이 없습니다";
        }
        return todo.toString();

    }
    public String markDone(int id) {
        Todo todo = repository.findById(id);
        if(todo == null){return"해당 할일이 없습니다";}
        todo.markDone();
        return"완료처리 되었습니다";
    }
    public String undoDelete(int id) {
        if(deleteStack.isEmpty()){return"삭제된 항목이 없습니다";}
        Todo restored =deleteStack.pop();
        repository.save(restored);
        return"복구완료";
    }
    public String getIncompleteTodos() {
        List<Todo> list = repository.findAll().stream()
                .filter(todo -> !todo.isDone())
                .collect(Collectors.toList());
        if (list.isEmpty()) return " 완료할 일이 없습니다.";
        return list.stream()
                .map(Todo::toString)
                .collect(Collectors.joining("\n"));
    }

    public String searchByKeyword(String keyword) {
        List<Todo> result = repository.findAll().stream()
                .filter(todo -> todo.getTask().toLowerCase().contains(keyword.toLowerCase()))
                .collect(Collectors.toList());
        if (result.isEmpty()) return " 검색 결과가 없습니다.";
        return result.stream()
                .map(Todo::toString)
                .collect(Collectors.joining("\n"));
    }

    public boolean isEmpty() {
        return repository.findAll().isEmpty();
    }
}

TodoRepository 생성 데이터 저장역할 (인터페이스)

규칙을 정함 (할일 저장소는 이런 기능을 가져야 함)

import java.util.List;
public interface TodoRepository {
    List<Todo> findAll();
    Todo findById(int id);
    void save(Todo todo);
    void delete(int id);
    void update(int id,Todo updateTodo);
}

실제 구현하는 MemoryTodoRepository
TodoRepository의 기능들을 실제로 구현시키는 클래스 

import java.util.*;
public class MemoryTodoRepository implements TodoRepository {
    private final List<Todo> todos = new ArrayList<>();

    @Override
    public List<Todo>findAll() {
        return new ArrayList<>(todos);
    }
    @Override
    public Todo findById(int id) {
        return todos.stream().filter(t -> t.getId() == id).findFirst().orElse(null);
    }
    @Override
    public void save(Todo todo){
        todos.add(todo);
    }
    @Override
    public void delete(int id){
        todos.removeIf(t ->t.getId() == id );
    }
    @Override
    public void update(int id,Todo updateTodo){
        delete(id);
        save(updateTodo);
    }
}

리뷰

자료구조를 공부하다 보니 알고리즘에 대해서 조금씩 알아가고 있다 버블정렬 선탤정렬
빅오 표기법 빅오를 알고나니 효율적인 코드를 써야겠다는 생각은 들었는데
이게 뭐가 효율적인지코테를 할때도 생각을 해야하는지 고민이 많아졌다
Java Spring에들어가기 앞서 RestApi의 구조를 이해하고
MVC구조를 이해하기 위해 전체 리팩토링 저장소 추상화를 했다
http 메소드들도 이해가 좀 됐고 api가 뭔지도 조금 알거같다
이제 gpt가 코드를 짜주면 애가 뭘이야기하는지 조금은 알거같다
요즘 많이 해이해졌는데 포기하지말고 계속하자 Spring가자!

profile
초심자

0개의 댓글