Brute Force, Optional, Static Factory Method

calis_ws·2023년 6월 16일
0

String

문자 인코딩 (Character Encoding)

특정 숫자(신호)에 특정 문자를 대응해두고, 문자나 기호를 컴퓨터에 신호 형태로 저장하는 과정을 Character Encoding이라고 한다.
(신호에서 문자를 해석하는 과정은 Decoding)

ASCII 코드

  • 7 비트 활용 (2^7 = 128) + 1 bit (패리티 비트)
  • 영문자 + 기호(NULL, TAB, BACKSPACE, ESC), 33개 출력 불가능 문자

유니코드

  • 여러 나라의 언어를 처리하기 위해 유니코드 탄생
  • 2 바이트 (16 비트)

문자열과 숫자 변환

문자열에서 숫자 만들기 (A to I)

public class AlphaToInteger {
    // 숫자로만 이루어진 value 문자열이 입력된다고 가정
    // 1. 각 글자를 숫자 데이텉로 해석
    // 2. 48을 빼주면 숫자가 된다.
    private int atoi(String value) {
        int result = 0;
        // TODO 문자열을 한글자(한 자리)씩 확인
        boolean negative = false;
        int i=0;

        // 첫 번째 문자가 '-' 인지
        if (value.charAt(i) == '-') {
            negative = true;
            i++;
        }

        for (; i < value.length() ; i++) {
            // TODO 자릿수 변환
            result *= 10;
            // TODO 글자를 숫자로 변환
            result += value.charAt(i) - '0';
        }

        if (negative) result *= -1;

        return result;
    }

    public static void main(String[] args) {
        AlphaToInteger atoi = new AlphaToInteger();
        System.out.println(atoi.atoi("12345"));
        System.out.println(atoi.atoi("-4291"));
    }
}

숫자에서 문자열 만들기 (I to A)

public class IntegerToAlpha {
    public String itoa(int value) {
        StringBuilder answerBuilder = new StringBuilder();

        // 음수인지 확인
        boolean negative = false;
        if (value < 0) {
            negative = true;
            value *= -1;
        }

        // TODO value가 0보다 큰 동안
        while (value > 0) {
            // TODO value를 10으로 나눈 나머지를 문자로 변환
            char digitChar = (char) (value % 10 + '0');
            answerBuilder.append(digitChar);

            // TODO value 나누기 10
            value /= 10;
        }

        if (negative) answerBuilder.append('-');

        return answerBuilder.reverse().toString();
    }

    public static void main(String[] args) {
        IntegerToAlpha itoa = new IntegerToAlpha();
        System.out.println(itoa.itoa(-1234));
    }
}

Brute Force Pattern Matching

Target 의 특정 위치부터, Pattern 문자열과 완전히 일치하는지 검사

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

// qwertyuiuiuytrertyuiopopoiuytrqwertyuytrertywqwertyuiuytrewqwertyuiiuiuiytrewert
// qwert
// 0, 30, 45, 59
public class BFPatternMatching {
    public void solution() throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String target = br.readLine();
        String pattern = br.readLine();

        int tarIdx = 0;
        int patIdx = 0;
        
        // TODO tarIdx 전체 길이보다 작을 동안에 반복한다
        // TODO 존재하는지만 검사하는 경우, patIdx가 pattern.length() 보다 작을 동안 반복한다.
        while (tarIdx < target.length() && patIdx < pattern.length()) {
            // TODO target[tarIdx]가 pattern[patIdx] 랑 다를 경우
            if (target.charAt(tarIdx) != pattern.charAt(patIdx)) {
                // TODO tarIdx를 여태까지 이동한 만큼 되돌린다 (i-j)
                tarIdx -= patIdx;
                // TODO patIdx를 -1로 할당한다.
                patIdx = -1;
            }
            // TODO 다음칸으로 이동한다.
            tarIdx++;
            patIdx++;
        }

        // TODO patIdx == patter.length() 이면 성공 > 어디에서 찾았는지 출력
        if (patIdx == pattern.length()) {
            System.out.println(tarIdx-patIdx);
        }
        // TODO 못찾은 경우, System.out.println("404 Not Found")
        else {
            System.out.println("404 Not Found");
        }
    }

    public static void main(String[] args) throws IOException {
        new BFPatternMatching().solution();
    }
}

Optional<>

  • null safety를 위한 클래스

  • 자바 8버전에 처음 소개된 컨테이너 객체

  • NullPointException을 try-catch로 예외 처리 하지 않고 Optional로 감싸 처리할 수 있게 해줌

JpaRepository에서 Optional

  • JpaRepository에서 한개의 객체를 반환하는 메소드 실행시 Optional<>로 반환
    단일 항목을 반환할 경우 Optional로 반환해준다.
  • Optional로 값을 저장하려면 ofNullable() 메서드가 필요하지만 JpaRepository에서 반환된 값은 바로 Optional 반환함

Optional<T>.orElse(T other) : null이면 T타입의 other 반환 null이 아니면 T타입 그대로 반환

Optional<T>.orElse(null)을 컨트롤러에 전달해서 404Error 를 의도적으로 발생시키는 방식으로도 사용 가능

Entity에서 DTO(Data Transfer Object) 분리하는 이유

  1. View Layer(Client)와 통신하는 DTO 클래스는 자주 변경되지만 테이블에 매핑되는 Entity는 그에 비해 변경도 적고, 영향 범위도 매우 크다.

  2. DTO가 일회성으로 데이터를 주고받는 용도로 사용되는 것과 다르게 Entity의 생명 주기(Life Cycle)도 전혀 다르다.

  3. 정보가 불일치 할 경우 : 테이블에 매핑되는 정보가 실제 View에서 원하는 정보와 다를 수 있다.
    이러한 경우에는 변환하는 로직이 필요한데, 같이 쓰게 된다면 해당 로직이 Entity에 들어가게 되어서 Entity가 지저분해진다.

  4. 정보 노출 : DB 로부터 조회된 Entity 를 그대로 View로 넘길 경우 불필요한, 노출되면 안 되는 정보까지 노출될 수 있고, 이를 막기 위한 로직을 따로 구현해야 한다.

Static Factory Method

  • 생성자 기법과는 별도로 생성만을 담당하는 클래스 메소드를 의미한다.

  • 직접적인 생성자가 아닌 메소드를 통해 객체를 생성한다.

  • 외부에서 요청이 들어오면 이에 맞게 객체를 생성하거나, 단순 반환해주는 것이 주된 역할이다.

팩토리 메서드의 장점

  1. 생성자와는 달리 팩토리 메서드에는 이름이 있다.
    (코드의 가독성이 높아지며 사용하기도 쉬워짐)

  2. 생성자와 달리 호출할 때마다 새로운 객체를 생성할 필요가 없다.
    (동일한 객체가 요청되는 일이 잦고, 객체를 만드는 비용이 클 때 적용시키면 성능을 크게 개선이 가능)

  3. 생성자와 달리 반환값 자료형의 하위 자료형 객체를 반환할 수 있습니다.
    (반환되는 객체의 클래스를 훨씬 유연하게 결정이 가능)

  4. 입력 매개변수에 따라 다른 클래스의 객체를 반환할 수 있습니다.

  5. 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됩니다.

Skeleton 프로젝트

StudentDto.class

package com.example.student.dto;

import com.example.student.entity.StudentEntity;
import lombok.Data;

@Data
public class StudentDto {
    private Long id;
    private String name;
    private Integer age;
    private String phone;
    private String email;

    // static factory method pattern
    public static StudentDto fromEntity(StudentEntity entity) {
        StudentDto dto = new StudentDto();
        dto.setId(entity.getId());
        dto.setName(entity.getName());
        dto.setAge(entity.getAge());
        dto.setPhone(entity.getPhone());
        dto.setEmail(entity.getEmail());
        return dto;
    }
}

StudentEntity.class

package com.example.student.entity;
/* CREATE TABLE students (
*   id INTEGER PRIMARY KEY AUTOINCREMENT,
*   name TEXT,
*   age INTEGER,
*   phone TEXT,
*   email TEXT
* */

import jakarta.persistence.*;
import lombok.Data;

@Data
@Entity
@Table(name = "students")
public class StudentEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Integer age;
    private String phone;
    private String email;
}

StudentRepository.interface

package com.example.student.repository;

import com.example.student.entity.StudentEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudentRepository extends JpaRepository<StudentEntity, Long> {
}

StudentController.class

package com.example.student;

import com.example.student.dto.StudentDto;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.server.ResponseStatusException;

@Controller
@RequestMapping("/students")
public class StudentController {

    private final StudentService service;

    public StudentController(StudentService service) {
        this.service = service;
    }

    @GetMapping("")
    public String home(Model model) {
        model.addAttribute("studentList", service.readStudentAll());
        return "home";
//        throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED);
    }

    // create.html 응답
    @GetMapping("/create-view")
    public String createView() {
        return "create";
//        throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED);
    }

    // 새로운 StudentEntity 생성 후 상세보기 페이지로
    @PostMapping("/create")
    public String create(StudentDto dto) {
        StudentDto newDto = service.createStudent(dto);
        return "redirect:/students/" + newDto.getId();
//        throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED);
    }

    // id에 해당하는 StudentEntity의 read.html 응답
    @GetMapping("/{id}")
    public String read(@PathVariable("id") Long id, Model model) {
        service.readStudent(id);
        model.addAttribute("student", service.readStudent(id));
        return "read";
//        throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED);
    }

    // id에 해당하는 StudentEntity의 update.html 응답
    @GetMapping("/{id}/update-view")
    public String updateView(@PathVariable("id") Long id, Model model){
        model.addAttribute("student", service.readStudent(id));
        return "update";
//        throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED);
    }

    // id에 해당하는 StudentEntity 수정 후 상세보기 페이지로
    @PostMapping("/{id}/update")
    public String update(@PathVariable("id") Long id, StudentDto dto) {
        StudentDto updateDto = service.updateStudent(id, dto);
        return "redirect:/students/" + updateDto.getId();
//        throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED);
    }

    // id에 해당하는 StudentEntity delete.html
    @GetMapping("/{id}/delete-view")
    public String deleteView(@PathVariable("id") Long id, Model model) {
        model.addAttribute("student", service.readStudent(id));
        return "delete";
//        throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED);
    }

    // id에 해당하는 StudentEntity 삭제 후 홈페이지로
    @PostMapping("/{id}/delete")
    public String delete(@PathVariable("id") Long id) {
        service.deleteStudent(id);
        return "redirect:/students";
//        throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED);
    }
}

StudentService.class

package com.example.student;

import com.example.student.dto.StudentDto;
import com.example.student.entity.StudentEntity;
import com.example.student.repository.StudentRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


@Service
public class StudentService {
    private final StudentRepository repository;

    public StudentService(StudentRepository repository) {
        this.repository = repository;
    }

    // CREATE
    public StudentDto createStudent(StudentDto dto) {
        StudentEntity newStudent = new StudentEntity();
        newStudent.setName(dto.getName());
        newStudent.setAge(dto.getAge());
        newStudent.setPhone(dto.getPhone());
        newStudent.setEmail(dto.getEmail());
        return StudentDto.fromEntity(repository.save(newStudent));
    }

    // READ
    public StudentDto readStudent(Long id) {
        Optional<StudentEntity> optionalEntity = repository.findById(id);
        if (optionalEntity.isPresent()) {
            return StudentDto.fromEntity(optionalEntity.get());
        } else throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }

    // READ ALL
    public List<StudentDto> readStudentAll() {
        // 모든 entity 조회 repository method
        List<StudentDto> studentDtoList = new ArrayList<>();
        List<StudentEntity> studentEntityList = this.repository.findAll();
        // 자유롭게
        for (StudentEntity entity:
             studentEntityList) {
            studentDtoList.add(StudentDto.fromEntity(entity));
        }
        return studentDtoList;
    }

    // UPDATE
    public StudentDto updateStudent(Long id, StudentDto dto) {
        Optional<StudentEntity> optionalEntity = repository.findById(id);
        if (optionalEntity.isPresent()) {
            StudentEntity targetEntity = optionalEntity.get();
            targetEntity.setName(dto.getName());
            targetEntity.setAge(dto.getAge());
            targetEntity.setPhone(dto.getPhone());
            targetEntity.setEmail(dto.getEmail());
            return StudentDto.fromEntity(repository.save(targetEntity));
        } else throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }

    // DELETE
    public void deleteStudent(Long id) {
//        repository.deleteById(id);
        if(repository.existsById(id))
            repository.deleteById(id);
        else throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }
}

Home

Create

Read

Update

Delete

인사이트 타임

배열 원소의 길이

https://school.programmers.co.kr/learn/courses/30/lessons/120854

class Solution {
    public int[] solution(String[] strlist) {
        int[] answer = new int[strlist.length];
        for (int i = 0; i < strlist.length; i++) answer[i] = strlist[i].length();
        return answer;
    }
}

n의 배수

https://school.programmers.co.kr/learn/courses/30/lessons/181937

class Solution {
    public int solution(int num, int n) {
        if (num % n == 0) return 1;
        else return 0;
    }
}

flag에 따라 다른 값 반환하기

https://school.programmers.co.kr/learn/courses/30/lessons/181933

class Solution {
    public int solution(int a, int b, boolean flag) {
        if (flag == true) return a + b;
        else return a - b;
    }
}

easy

태환님의 문제 선정은 늘 옳다.

review

전체적으로 어려운 내용은 없었으나 여전히 익숙하지 않은 Spring의 실습은 하루종일 나를 피곤하게 만들었다. Students 프로젝트를 두 번째 해보지만 자주 들려오는 단어들(Entity, Optional, Dto ...)은 가끔 들리면서도 정확하게 무슨 의미인지는 아직도 잘 모르겠다.

이번 주는 너무나 힘들었던 것 같다. 수업듣고나서 밥먹고 포스팅만하고 씻고 잠만 자는데도 하루종일 피곤하고 정신이 없었던 것 같다. 어쩌다보니 팀원들과 다음 주에 아기사자반을 한 번 들어보기로 했다. 일단 금요일은 쉬고 주말에 복습을 해둬야겠다. 이번 주도 고생했다 우스가

profile
반갑습니다람지

0개의 댓글