Spring이란?
: 자바로 웹 서비스를 만들 때 사용하는 개발 도구(프레임워크)
Spring 없이 만들면:
객체 생성 직접 해야 함
코드가 복잡해짐
유지보수 어려움
지금은 Spring을 사용해도 어렵다고 느껴지지만 나중 가면 갈수록, 프로젝트가 커질수록 Spring을 사용하면 관리가 쉽다고 느껴지게 된다
문제점1. 자바 17 설치 및 연동
처음에 자바 11로 잘못 다운받고 다시 자바 17을 오라클에서 다운받고 설치까지는 했는데.. 검색해보니까 시스템 환경변수 설정에 들어가서 경로설정도 해야한대서 하라는대로 했다고 생각했는데 index.html을 실행시키려고만 하면 404 NOT FOUND ERROR 문자가 떴다..
이틀동안 쳇 GPT와 씨름하며 해결하려고 했지만 나혼자 해결하지 못하고 지인님께 도움을 받았다(정말 압도적 감사합니다....)
핵심적인 문제는 자바는 17로 다시 다운 받았으면서 스프링은 자바 11 상태로 내버려두었던 것이 첫번째고 아래 사진처럼 SpringApplication을 통해 실행시켜야 했는데 그러지 다른 demo1이라는 것을 통해 실행시킨 것이 두번째인 것 같다.

인텔리제이의 html 기본 세팅에서 와 사이에 작성한 말이 웹 페이지에 그대로 나타난다. 이를 정적컨텐츠를 제공한다고 한다. (가공되지 않은 파일 자체를 반환)

사용자가 localhost:8080/hello-static.html 요청 -> 톰캣이 요청 전달(Spring에 내장) -> 톰캣이 컨트롤러를 찾음(동적처리) -> 없으면 정적 파일 resources/static/hello-static.html 확인 -> 파일 그대로 응답(코드실행X)
+동적 컨텐츠
동적 컨텐츠란 정적 컨텐츠와 반대되는 것으로 사용자의 요청에 따라 내용이 바뀌는 앱 컨텐츠이다.
MVC: 각 Model, View, Controller으로 탬플릿의 역할을 나눠서 프로그램을 만드는 구조라고 할 수 있다. (가공된 HTML 반환)
-> 관심사를 분리하여 코드의 유지보수성을 높이기 위함
Controller: /hello가 입력되면 hello spring 페이지 보여주기

Model: Controller가 View에 넘겨줄 데이터

View: 사용자에게 보여지는 HTML


API란? view를 사용하지 않고 객체(데이터)를 반환하는 방식
-> 프론트엔드와 분리하기 위해
@ResponseBody: 화면(view)를 쓰지 않고 데이터로 바로 응답하라는 어노테이션
-> return 값을 그대로 HTTP 응답 바디에 넣음
이때 객체 변환은 담당하는 것이 json이다. json 말고도 XML을 사용할 수 있지만 가볍고 빠른 장점을 가진 json을 표준적으로 사용한다.

@AfterEach: 메모리 DB에 저장된 데이터 삭제(@AfterEach 만날 때마다 실행)
한번에 여러 테스트를 실행하면 메모리 DB에 직전 테스트의 결과가 남을 수 있음
-> 이전 테스트 때문에 다음 테스트가 실패할 가능성이 생긴다.

Ctrl+shift+t: test 코드를 일일히 작성하지 않아도 class 이름 부분에 커서를 놓고 단축키를 실행시키면 테스트 파일이 만들어진다.
//given //when //then: '주어진 상황에서(given) 무언가를 실행했을 때(when) 나와야 하는 결과(then)'로 주석을 나누어 확인하는 것

@BeforeEach: 각 테스트 실행 전에 호출, 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고 의존관계를 맺어준다
스프링 빈: 스프링이 만들어주고 관리해주는 객체
우리가 new를 통해서 객체를 만들지 않아도 됌
왜 써야할까?
- 우리가 직접 객체 일일히 선언하면 관리가 복잡해짐
- 클래스 안에 객체를 선언하는 경우 둘의 의존관계가 지나치게 강해져 수정이 힘든 경우가 있음
- 의존성 주입(DI)을 쉽게 할 수 있음 -> 코드가 깔끔해짐
1 @Component 붙은 클래스 발견
2 객체 만들어서 컨테이너에 저장
3 @Autowired 발견
4 필요한 타입 확인
5 같은 타입 객체 찾아서 넣어줌
자바 코드로 직접 스프링 빈 등록(수동) vs 컴포넌트 스캔(자동)
@Configuration public class AppConfig { @Bean public MemberService memberService() { return new MemberService(); } }명확하고 제어가 가능 BUT 코드가 복잡해짐
@Component public class MemberService { }단순하고 간편함 BUT 어디서 등록되었는지 명확하지 않음
문제점2.
H2 database가 설치가 안됌..
여기서 또 시간을 보낼 수 없어서 여기서 강의 보는 것을 중단하고 CRUD 구현으로 넘어갔다(도와주세요..ㅜㅜ)
CRUD란 소프트웨어에서 데이터를 다룰 때 가장 기본이 되는 4가지 작업을 묶어서 부르는 용어로 각 철자는 아래를 의미한다.
C: create(생성) -> 새로운 데이터 추가
R: Read(읽기) -> 저장된 데이터 조회
D: Update(갱신) -> 기존 데이터 수정
D: Delete(삭제) -> 데이터 삭제
앞서 했던 회원관리 예제 만들기도 CRUD를 이용한 예제였다.
나는 CRUD를 이용해 상품 관리 시스템을 만들어 보았다.
C: 상품 추가
R: 상품 조회
U: 상품 이름 수정
D: 상품 품절시 상품 삭제

새 프로젝트를 생성하여 프로그램을 만들어보았다.
강의를 다 들었을 때는 Spring의 응답 구조가 모호하게 기억에 남았는데 직접 프로젝트를 생성하기 위해 구조를 생각하다보니 보다 명확하게 Spring의 응답 구조를 이해할 수 있었다.
사용자 입력 -> Controller -> Service -> Repository -> 수정 완료 -> 다시 목록으로 이동
Product 클래스를 생성하여 프로젝트에 상품 번호와 이름, 품절여부가 존재한다는 틀을 만들어주었다.
package hello.Hello_Spring.domain;
public class Product {
private Long id; //상품번호
private String name; //상품이름
private boolean soldOut; //품절여부
//기본 생성자
public Product(){
}
public Product(Long id, String name, boolean soldOut){
this.id = id;
this.name = name;
this.soldOut = soldOut;
}
//getter setter
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isSoldOut() {
return soldOut;
}
public void setSoldOut(boolean soldOut) {
this.soldOut = soldOut;
}
}
getter setter
: getter는 값을 꺼내고 setter는 값을 넣을 수 있음
자바에서 변수는 보통 안전성을 위해 private으로 설정되어있다.
따라서 외부에서 변수에 직접 접근하는 것이 불가능한데 이때 getter setter를 통해 값을 불러오고 새로 입력할 수 있게 된다. Alt+Insert 단축키를 통해 getter setter를 쉽게 불러올 수 있고 전체 선택시 자동으로 코드가 입력되어 편하다
(강의 들을 때는 몰랐는데 일일히 입력 안해도 되니까 엄청 편리한것 같다!!)
저장소 클래스 만들기:
repository 패키지 안에 ProductRepository라는 클래스를 선언하여 데이터를 저장하는 클래스를 만들었다.
package hello.Hello_Spring.repository;
import hello.Hello_Spring.domain.Product;
import org.springframework.stereotype.Repository;
import java.util.*;
@Repository //저장소 선언
public class ProductRepositoy {
private final Map<Long, Product> store = new HashMap<>(); //Long-상품번호, Product-실제상품정보
private Long sequence = 0L; //상품번호가 자동으로 하나씩 증가함
//상품 저장
public Product save(Product product){
sequence++;
product.setId(sequence);
store.put(product.getId(),product);
return product;
}
//전체 상품 조회
public List<Product> findAll(){
return new ArrayList<>(store.values());
}
//상품 1개 조회(id를 통해)
public Product findById(Long id){
return store.get(id);
}
//상품 수정
public Product update(Long id, Product updatedProduct){
Product findProduct = store.get(id);
if (findProduct != null){
findProduct.setName(updatedProduct.getName());
findProduct.setSoldOut(updatedProduct.isSoldOut());
}
return findProduct;
}
//상품 삭제
public void delete(Long id){
store.remove(id);
}
}
Repository와 Controller를 연결하기 위해 ProductService 클래스를 만들었다.
package hello.Hello_Spring.service;
import hello.Hello_Spring.domain.Product;
import hello.Hello_Spring.repository.ProductRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public Product addProduct (Product product){
return productRepository.save(product);
}
public List<Product> getALlProducts() {
return productRepository.findAll();
}
public Product getProduct(Long id){
return productRepository.findById(id);
}
public Product updateProduct(Long id, Product product){
return productRepository.update(id, product);
}
public void deleteProduct(Long id){
productRepository.delete(id);
}
}
상품정보를 입력받기 위해 controller 패키지에 ProductController 클래스를 생성하였다.
{
private final ProductService productService;
public ProductController(ProductService productService){
this.productService = productService;
}
//상품 목록화면
@GetMapping
public String list(Model model){
List<Product> products = productService.getALlProducts();
model.addAttribute("products", products);
return "productList";
}
//상품 등록화면
@GetMapping("/new")
public String createForm(){
return "productForm";
}
//상품 등록처리
@PostMapping("/new")
public String create(Product product){
productService.addProduct(product);
return "redirect:/product";
}
//정보 삭제
@PostMapping("/delete/{id}")
public String delete(@PathVariable Long id) {
productService.deleteProduct(id);
return "redirect:/products";
}
//상품 추가
@GetMapping("/edit/{id}")
public String editForm(@PathVariable Long id, Model model) {
Product product = productService.getProduct(id);
model.addAttribute("product", product);
return "productEdit";
}
//상품 수정
@PostMapping("/edit/{id}")
public String edit(@PathVariable Long id, Product product) {
productService.updateProduct(id, product);
return "redirect:/products";
}
}
링크를 입력하니 페이지로 잘 이동하였다.

문제점3.
이름을 입력하고 등록 버튼을 누르니 저런 에러페이지가 떴다.
또GPT에게 이게 무슨 에러냐고 물어보니 경로 이름이 맞지 않아서 생긴 에러라고 한다..
빨간 밑줄은 없었는데 어디가 잘못된 걸까?????

목록 보기는 잘 작동하는 것을 확인할 수 있었고 상품 삭제 또한 정상적으로 기능하였다.
하지만 수정을 누르니 또 에러가 발생하였다...
상품 등록부분에 에러가 있는 것 같다

품절을 누르면 사진처럼 품절 카테고리에 true라고 입력된다.
ㅜㅜ자바 코딩을 하다가 막힌 것도 아니고 스프링 부트를 아예 실행조차 못해서 과제를 못할 뻔했다는게 정말 무서운 사실이다..ㅋㅋㅋㅋ
코딩하다 잘못된건 아직 간단한 코드여서 그런지 밑줄도 쳐주고 에러 메세지도 있어서 어디가 문제인지 알 수 있고 나머지는 쳇GPT랑 말해보면 되는데 이런 환경설정??은 어디서부터 어떻게 어디가 잘못된건지를 아직 잘 모르겠어서 어떻게 물어봐야 할지도 모르겠고 어디를 보여줘야 할지 막막해서 힘들다.
동아리 들어오길 정말 잘했다🥲🥲 코딩만렙사이쪼렙이되...
스프링 강의를 들으면서 자꾸 멍을 때리게 되는 느낌이다.
이제 슬슬 어렵고 이해가 안가기 시작해서인가 조금 정신을 놓으면 강사님은 저만치 가있고 나는 지금 저분이 뭘한거지? 상태ㅜㅜ 차라리 배속을 해서 들으면 강사님 말에 더 집중하게 되는 기분이다.
그만큼 여러번 되감기 해야 하긴 하지만
처음보다 강의시간 짧아져서 좋아했는데 내용은 호락호락하지 않네😂
이번에 Spring과 하찮지만 html을 구현해본 결과 절대 이건 한번 해가지고는 기억에 남지 않을 것 같다는 생각이 들었다.
매일매일 꾸준히 해야 손가락이 기억해서 할 수 있을 것 같은 느낌
지금은 코드 입력을 하면서도 이게 뭐지 이건 왜 쓰는 거지 하는 부분들이 있다.
일단 이번엔 시간이 없어서 그냥 치긴 했지만 이렇게 넘어가면 다음엔 더 모를 것 같다..ㅜㅜ
공부를 더 해야할 것 같다
아직 갈길이 멀구나... 힘내자!!!!!
수빈님 고생하셨습니다 !! 항상 스프링을 시작할 때면 수많은 트러블 슈팅을 마주하게 됩니다 ... 저도 똑같이 겪었던 트러블 슈팅도 여러부분 있어서 정말 공감되고 눈물나는 벨로그였습니다 ... 그리고 몇가지 도움을 드리자면
h2 db는 굳이 컴퓨터에 다운로드 하실 필요 없습니다 ! 기본적으로 스프링에서 h2를 지원하기 때문에 의존성에 추가해줘서 빌드하면 굳이 컴퓨터에 설치 필요 없이 바로 사용 가능합니다.
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
다음 코드를 build.gradle에 추가한 후에 고래를 한 번 돌려주시면 이 부분 해결 가능할 것 같아요 !
그리고 컨트롤러 클래스 상단에 보면 상품 목록화면에 @GetMapping 은 되어있지만 엔드포인트 설정이 안되어있어서 오류가 나는 것 같습니다 ! @GetMapping("/products") 같은 엔드포인트를 설정해줘야할 것 같아요 !
위의 해결방법으로 해결이 안되면 따로 카톡 해주세요 !!!
수빈님 벨로그를 보면 항상 같이 하는 기분이라서 볼때마다 즐거운 것 같아요. ㅎㅎ 특히 저도 과제를 하면서 느꼈던 감정들이 저 혼자가 아니라는 것에 위로를 받기도 합니다..ㅎㅎ 4주차 너무나도 수고 많으셨습니다 !!!!