tuter77

2023.01.16 TIL 본문

TIL

2023.01.16 TIL

tuter77 2023. 1. 16. 18:37

<목차>

  1. <공부계획표>
  2. Java
  3. Group Study

<공부계획표>

13:00 ~ 14:00 그룹스터디 게시판 웹 구현           

14:00 ~ 18:00 (3시간) 온라인강의 시청.

           14:00 ~ 15:00 Java 강의시청

           15:00 ~ 16:00 Java 강의시청

           17:00 ~ 18:00 Java 강의시청

18:00 ~ 19:00 식사시간

19:00 ~ 22:00 Java 실시간 강의 시청

           19:00 ~ 20:00 Java 강의시청

           20:00 ~ 21:00 Java 강의시청

           21:00 ~ 22:00 Java 강의시청

22:00 ~ 23:00 블로그 작성 및 프로그래머스 문제 풀기.


<Java>

● 인터페이스

▷ 실습

책 선반 클래스 만들어서 책넣기, 빼기 기능 구현.

 

먼저 Shelf라는 상위 클래스를 만들어 필요한 객체와 기능을 구현한다.

package ch15;

import java.util.ArrayList;

public class Shelf {

	protected ArrayList<String> shelf;
	
	// 위에서 생성자를 써도 되지만 public인 아래의 멤버변수로 생성자를 써주는게 낫다.
	public Shelf() {
		shelf = new ArrayList<>(); //string 안써도됨.
		
	}
	
	public ArrayList<String> getShelf(){
		return shelf;
	}
	
	public int getCount() {
		return shelf.size();
	}
}

이 클래스는 상속받아야하기에 protected로 String을 매개로 받는 ArrayList > shelf객체를 생성하고 해당 객체는 아래에 pubic 생성자로 초기화한다.

 

생성된 객체는 아래의 getShelf로 저장된 값을 가져올 수 있고, getCount 메서드로 들어있는 책이 몇권인지 확인할 수 있다.

getCount() 메서드에서는 ArrayList의 size() 메서드로 저장된 값의 크기를 반환한다.

 

다음 Queue라는 인터페이스를 생성해 필요한 메서드를 정의한다.

package ch15;

public interface Queue {

	void enQueue(String title);
	String deQueue();
	
	int getSize();
}

이 인터페이스에서 제목을 매개변수로 받아 책을 저장하는 enQueue()와 책을 빼내는 deQueue(), 그리고 책이 몇권들었는지 int값을 반환해주는 getSize()를 명시해놓는다. 

 

이후 BookShelf 클래스를 만들어 이미 만들어둔 Shelf클래스를 상속하고, 인터페이스를 implements한다.

package ch15;

public class BookShelf extends Shelf implements Queue{

	@Override
	public void enQueue(String title) {
		shelf.add(title);
	
	}

	@Override
	public String deQueue() {
		return shelf.remove(0);
	}

	@Override
	public int getSize() {
		return getCount(); //getCount가 상위클래스에 구현되어있기때문에
	}

	
}

이때 인터페이스에서 정의된 메서드들을 오버라이딩하는데, enQueue()는 ArrayList의 add기능을 활용하여 책 제목을 저장하는 기능을 가진다.

다음 deQueue()는 ArrayList의 remove기능을 활용해 인덱스값이 0번째인 데이터(책제목)를 반환하고 해당 데이터를 삭제한다.

마지막으로 getSize()는 상위클래스에서 정의해놓은 getCount() 메서드를 반환해 책이 몇권들었는지 알려준다.

 

테스트클래스.

package ch15;

public class BookShelfTest {

	public static void main(String[] args) {

		Queue bookQueue = new BookShelf();
		bookQueue.enQueue("토지1");
		bookQueue.enQueue("토지2");
		bookQueue.enQueue("토지3");
		bookQueue.enQueue("토지4");
		bookQueue.enQueue("토지5");
		
		System.out.println(bookQueue.getSize());
		System.out.println(bookQueue.deQueue());//호출될때마다 한권씩 제거됨.
		System.out.println(bookQueue.deQueue());
		System.out.println(bookQueue.deQueue());
		System.out.println(bookQueue.deQueue());
		System.out.println(bookQueue.deQueue());
		System.out.println(bookQueue.getSize());
		
	}

}

테스트클래스에서 인스턴스를 생성하고, 해당 인스턴스에 5권의 책제목을 저장한다.

 

이후 몇권이 들어있는지 확인한 뒤, 책을 한권한권 꺼내며 인스턴스에서 지워준다.

 

결과창.

 

5권의 책을 모두 꺼내서 0권이 된 것을 볼 수 있다.

 

실습.

▷ gameLevel

Player가 있고, Player는 GameLevel속성을 가진다.GameLevel 단계마다 run(), jump(), turn() 세가지 기능이 업그레이드 된다.초보자 레벨 : 천천히 달립니다.(run() 만 가능)

중급자 레벨 : 빠르게 달리고, 점프할 수 있습니다.(run(), jump() 가능)

고급자 레벨 : 매우 빠르게 달리고, 높게 점프하며, 턴 할 수 있습니다.(run(), jump(), turn() 가능)

 

player는 한번에 하나의 레벨 상태만을 가질 수 있다.player가 play()중에 레벨에 있는 go(int count)라는 메서드를 호출하면 run()하고 count횟수만큼 jump()하며, turn()한다.아래 클래스 다이어그램을 참고해 각 레벨에서 go()가 호출될 때 아래와 같이 출력되도록 해야한다. 

먼저 중심이되는 PlayerLevel클래스를 추상클래스로 구현한다.

package ch16.gameLevel;

public abstract class PlayerLevel {

	public abstract void run();
	public abstract void jump();
	public abstract void turn();
	public abstract void showLevelMessage();
	
	final public void go(int count) {
		run();
		for(int i=0; i<count; i++) {
			jump();
		} 
		turn();	
	}
		
}

run(), jump(), turn(), showLevelMessage() 메서드를 정의하고 아래에 시나리오를 담고있는 go()메서드를 정의한다.

jump()메서드는 count의 숫자만큼 반복하기때문에 for문을 이용한다.

각 메서드는 여기서 불리는 것이아니라, Player클래스에서 다른 메서드로 호출할 것이다.

 

다음은 객체 생성의 중심이 될 Player 클래스를 만든다.

 

package ch16.gameLevel;

public class Player{

	private PlayerLevel level;
	
	public Player() {
		level = new BeginnerLevel();
		level.showLevelMessage();
	}
	
	public PlayerLevel getLevel() {
		return level;
	}

	public void upgradeLevel(PlayerLevel level) {
		this.level = level;
		level.showLevelMessage();
	}
	
	public void play(int count) {
		level.go(count);
	}
}

여기서 좀 헤맸는데, 인터페이스나 추상클래스로 선언을 해야할지 그냥 클래스로 만들어야할지 몰라서 다 시도해보다 일반클래스로 만들었다.

결국에 기능구현이 되어있어야하기 때문에 일반 클래스로 만들게 되었다.

PlayerLevel 클래스의 객체를 하나 생성하여 각 메서드 내에서 PlayerLevel의 메서드들을 호출할 수 있도록했다.

Player() 메서드에서 level객체를 PlayerLevel을 상속하는 BegunnerLevel()의 인스턴스로 지정한다. (시작할때는 무조건 비기너)

그리고 PlayerLevel의 showLevelMessage()메서드를 이용해 레벨을 한번 출력해준다.

 

getLevel은 현재의 레벨을 반환해준다.

upgradeLevel()은 파라미터로 PlayerLevel 타입의 level 변수를 매개변수로 받는다. 해당 매개변수의 showLevelMessage()메서드를 호출한다. 

이는 level 변수를 바꿀 때마다 호출할 것이다.

 

play()는 메인 기능으로 level변수의 go기능을 호출하게 된다. 

test에서 Player안의 기능만 사용하여 PlayerLevel의 기능들을 호출하게끔 구성했다.

 

다음으로 각 레벨의 클래스를 정의한다.

package ch16.gameLevel;

public class BeginnerLevel extends PlayerLevel{


	@Override
	public void run() {
		System.out.println("천천히 달립니다.");
	}

	@Override
	public void jump() {
		System.out.println("jump 못하지롱");		
	}

	@Override
	public void turn() {
		System.out.println("turn 못하지롱");	
	}

	@Override
	public void showLevelMessage() {
		System.out.println("****** 초급자 레벨입니다. ******");		
	}

}
package ch16.gameLevel;

public class AdvancedLevel extends PlayerLevel{
	
	@Override
	public void run() {
		System.out.println("빨리 달립니다.");
	}

	@Override
	public void jump() {
		System.out.println("높이 jump 합니다.");		
	}

	@Override
	public void turn() {
		System.out.println("turn 못하지롱");	
	}

	@Override
	public void showLevelMessage() {
		System.out.println("****** 중급자 레벨입니다. ******");	
	}

}
package ch16.gameLevel;

public class SuperLevel extends PlayerLevel{
	
	
	@Override
	public void run() {
		System.out.println("엄청 빠르게 달립니다.");
	}

	@Override
	public void jump() {
		System.out.println("아주 높이 jump 합니다.");		
	}

	@Override
	public void turn() {
		System.out.println("turn 합니다.");	
	}

	@Override
	public void showLevelMessage() {
		System.out.println("****** 고급자 레벨입니다. ******");		
	}
}

위의 세 코드를 보면, 모두 extends 키워드로 PlayerLevel 추상 클래스를 상속하고 있고 해당 메서드들을 각 타입에 맞게 재정의하고 있다.

 

이제 테스트 해보면 된다.

package ch16.gameLevel;

public class LevelTest {

	public static void main(String[] args) {

		Player player1 = new Player();
		player1.play(1);
		
		AdvancedLevel aLevel = new AdvancedLevel();
		player1.upgradeLevel(aLevel);
		player1.play(2);
		
		SuperLevel sLevel = new SuperLevel();
		player1.upgradeLevel(sLevel);
		player1.play(3);
	}

}

생성자를 만들어 Player타입의 Player 클래스의 player1이라는 인스턴스를 생성하는데 아무것도 없이 이렇게 생성하면 기초값은 BeginnerLevel로 지정되어 있기때문에 이는 비기너 레벨을 가진 플레이어가 된다.

play메서드를 호출해서 파라미터에 1만큼 count를 주었기에 jump메서드가 1회 반복될 것이다.

 

이후 AdvancedLevel 타입으로 aLevel이라는 인스턴스를 생성해 upgradeLevel에 넣어주면 player1 인스턴스는 AdvancedLevel 타입의 메서드들을 호출하게된다.

 

같은 방식으로 SuperLevel까지 작성한뒤 결과를 돌려주면 아래와 같다.

 

위의 결과창을 보면 count 만큼 jump가 반복되었고 각 타입에 맞게 메서드가 호출되었음을 확인할 수 있다.

 


<Group Study>

그룹스터디 게시판 웹 구현

▷ CRUD 중 U

저번주에 진행하던 DB에서 데이터를 받아와 웹 브라우저에 띄워주는 작업을 마무리했다.

조회, 새 글 저장, 기존 글 삭제 는 구현했지만 왜인지 수정기능이 작동하지 않았었는데 알고보니 어노테이션을 잘못 붙였었다.PostMappting으로 해야 정보를 새로 저장하는 것이 가능한데 데이터를 가져오기만 하는 겟매핑 어노테이션으로 시도하고 있었던것....(멍청한 나.. 오탈자와 기본기에서 너무 모자라다.)정상작동하게 된 코드는 아래와같다.

 

먼저 상세페이지.

 

이 브라우저에서 해당 이름을 클릭하면 상세정보를 볼 수 있는 상세페이지로 넘어가는데 상세페이지는 아래와 같이 구현했던걸, 저번주에 공부했다.

그 중 수정페이지로 넘어가는 버튼을 상세페이지 안에 구현했다.

이 수정버튼을 누르면 수정할 수 있는 html페이지로 넘어가게 된다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>상세페이지</title>
</head>

<body>
    <h1 th:text="${healthInfo.brand_name}"></h1>
    <p th:text="${healthInfo.land_number}"></p>
    <p th:text="${healthInfo.road_number}"></p>
    <p th:text="${healthInfo.category}"></p>
    <a th:href="@{/home/modified/{id}(id=${healthInfo.id})}">수정</a>
    <a th:href="@{/home/delete(id=${healthInfo.id})}">삭제</a>
    <button type="submit" onclick="location.href='/home'">뒤로</button>
</body>
</html>

 

여기서 a태그를 사용해 링크를 걸었고, 해당 링크는 id값을 통해 수정페이지에 데이터를 불러온다.

수정 페이지는 아래와 같다.

이 페이지는 아래 html로 구현했고, 요청은 컨트롤러에서 처리된다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>상세페이지</title>
</head>

<body>
    <form th:action="@{/home/modified/update/{id}(id=${healthInfo.id})}" method="post">
        <input name="brand_name" type ="text" th:value="${healthInfo.brand_name}">
        <input name="land_number" type ="text" th:value="${healthInfo.land_number}">
        <input name="road_number" type ="text" th:value="${healthInfo.road_number}">
        <input name="category" type ="text" th:value="${healthInfo.category}">
        <button type="submit">수정</button>
        <a th:href="@{/home}">홈으로</a>
    </form>
</body>
</html>

id를 통해 각 값들을 th:value라는 키워드로 조회해서 가져온다.

그리고 수정버튼을 누르면 수정기능이 수행되고 '홈으로' 버튼을 누르면 메인페이지로 돌아가게 된다.

형태 자체는 writer.html 과 같지만, id를 통해 안에 값들을 불러서 보여준다는 점이 다르다.

 

상세페이지에서 수정버튼을 눌러 modified.html 페이지를 반환해주고 해당 데이터를 가져오는 컨트롤러의 기능은 아래와 같이 구현했다.

    @GetMapping("/home/modified/{id}") //상세 페이지 내 수정
    public String modified(@PathVariable("id") Long id, Model model) {
        model.addAttribute("healthInfo", homeService.healthView(id));
        return "modified";
    }

여기서 @Pathvariable이 또 보이는데 id값을 파라미터로 전달해주는 역할을 한다.

model객체에 전달된 id값은 homeService의 healthView메서드로 데이터를 조회해서 값을 저장하고 이를 modified 페이지에 보여주게된다.

 

값을 수정해서 새로 저장하여 보여주는 기능은 아래와 같이 구현했다.

    @PostMapping("/home/modified/update/{id}")
    public String modifiedUpdate(@PathVariable("id") Long id, HealthInfo healthInfo) {
        HealthInfo healthInfoTemp = homeService.healthView(id); //id값으로 조회한 데이터 가져옴

        healthInfoTemp.setBrand_name(healthInfo.getBrand_name());
        healthInfoTemp.setLand_number(healthInfo.getLand_number());
        healthInfoTemp.setRoad_number(healthInfo.getRoad_number());
        healthInfoTemp.setCategory(healthInfo.getCategory());

        homeService.write(healthInfo);

        return "redirect:/home";
    }

여기가 문제의 GetMapping 구간이었는데 이를 데이터를 저장할 수 있는 @PostMapping 어노테이션으로 바꾸어주니 정상 작동하게 되었다.

이 modifiedUpdate() 메서드는 패스베리어블로 id값을 동일하게 전달 받고 이를 healthinfo 객체에 저장하여 첫 생성자 구문을 통해 값을 반환하게 된다.

 

healthInfo의 인스턴스 healthInfoTemp 는 반환된 값들을 갖고있고 해당 인스턴스에서 get/set 메서드를 통해 값들을 수정하게 된다.

 

해당 값들이 객체에 잘 저장되었으니 db까지 저장시켜주는 코드는 homeService의 write 메서드이다. 이는 앞서 구현한 저장기능과 동일하다.

결과적으로 수정이 잘되면 메인 페이지(home)를 재요청한다.(브라우저에 보여준다)

 

'TIL' 카테고리의 다른 글

2023.01.19 TIL  (0) 2023.01.19
2023.01.17 TIL  (0) 2023.01.18
2023.01.13  (0) 2023.01.13
2023.01.12 TIL  (0) 2023.01.12
2023.01.11 TIL  (0) 2023.01.11