0801 ~ 0802

내가 우선 css, html 을 부트스트랩을 이용해 디자인을 한 후 팀원에게 넘겨줬다.

구체적인 디자인 작업을 하는 팀원 1, 

파일정의서, 화면정의서 등 작업을 하는 팀원 2(나),

컨트롤러 구현을 하는 팀원 3. 이렇게 역할을 분담해 작업을 시작했다. 

 

0802 ~ 0803

다른 팀원이 컨트롤러 구현에서 어려움이 많아 내가 다시 파일 구조를 만들고 처음부터 컨트롤러 구현을 해나갔다. 

컨트롤러 구현을 하며 나 뿐만 아니라 모든 팀원이 스프링에 대한 이해도가 낮을 것을 확인할 수 있었다. 

이 기회에 열심히 공부했고 다른 팀원에게도 도움이 될까 싶어 노션에 컨트롤러 구현 과정을 적어 공유했다. 

 

 

팀 노션에서 공유한 부분 발췌 ) 


스프링 공부 _ 컨트롤러 구현과정

 

절대 이 영역 수정하지 말 것 !   

-> 노션을 공유해서 사용하면 내가 쓴 글이 수정되는 일들이 종종 있어 이렇게 적어두었다.

 

공부를 위해 이 과정을 처음부터 따라가주셔도 좋을 것 같습니다.

구현한 공룡게임 코드는 조 단톡방에 0802(금)에 올려드렸습니다.

 

  • 페이지 이동 구조
  1. 로그인 후 아이디와 비밀번호가 일치하면 게임페이지로 이동
  2. 로그인 후 아이디, 비밀번호 둘 중 하나라도 일치하지 않으면 로그인 페이지로 이동
  3. 회원가입 버튼을 누르면 회원가입 페이지로 이동
  4. 회원가입 정보 기입 후 가입하기 버튼을 누르면 자동로그인, 게임페이지로 이동
  5. 회원가입 후 로그인 페이지에서는 다시 1, 2번 참조
  6. 랭킹 버튼을 누르면 랭킹 페이지로 이동

 

  • 컨트롤러 구현

패지지와 폴더 구조가 중요한게 아님

패키지명은 작업 관리의 유지보수성 극대화를 위해 임의로 정한 것.

어떤 패키지에 어떤 클래스가 들어가야하는지에 너무 고민할 필요가 없었다

 

RootController 에서 페이지 이동 처리

회원 가입 화면 요청

회원 가입 요청 (가입시켜줘!)

로그인 화면 요청

로그인 정보 확인 요청 (아이디, 비밀번호 맞는지 폼값을 넘겨 확인)

로그인 정보 불일치 시 다시 로그인 화면으로 이동

 

이걸 코드로 옮기면

 

∨ homecontroller 로 실험해본 코드

더보기
package com.dino.root;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class RootController {
	
	private static final Logger logger = LoggerFactory.getLogger(RootController.class);
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
	
	@RequestMapping(value = "signup", method = RequestMethod.GET)
	public String signup() {
		System.out.println("signup");
		return "signup";
	}
	
	@RequestMapping(value = "signup", method = RequestMethod.POST)
	public String signup2(HttpServletRequest req) {
		System.out.println("signup 회원가입 정보를 저장");
		System.out.println(req.getParameter("aaa"));
		
		return "home";
	}
	
}

 

homecontroller 에서 수정된 RootController.java

package com.dino.root;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class RootController {
    
    private static final Logger logger = LoggerFactory.getLogger(RootController.class);

    // 메인 페이지 이동
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home() {
        logger.info("Welcome to the home page!");
        return "home";
    }
    
    // 로그인 페이지 이동
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login() {
        return "login";
    }

    // 로그인 요청 처리
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String loginPost(@RequestParam("userid") String userid,
                            @RequestParam("password") String password,
                            HttpServletRequest req) {
        // 아이디와 비밀번호 확인 (예제로 확인용)
        if ("user".equals(userid) && "password".equals(password)) {
        	return "redirect:/game";
        } else {
            return "login";
        }
    }

    // 회원가입 페이지 이동
    @RequestMapping(value = "/signup", method = RequestMethod.GET)
    public String signup() {
        return "signup";
    }

    // 회원가입 요청 처리
    @RequestMapping(value = "/signup", method = RequestMethod.POST)
    public String signupPost(HttpServletRequest req) {
        // 회원가입 처리 로직 (여기서는 예제로 간단하게 출력만 함)
        String userid = req.getParameter("userid");
        String password = req.getParameter("password");
        logger.info("회원가입한 회원 이름: {} 비밀번호: {}", userid, password);

        // 회원가입 후 바로 로그인 처리
        if ("user".equals(userid) && "password".equals(password)) {
            return "redirect:/game";
        } else {
            return "login";
        }
    }

    // 랭킹 페이지 이동
    @RequestMapping(value = "/ranking", method = RequestMethod.GET)
    public String ranking() {
        return "ranking";
    }

    // 게임 페이지 이동 (로그인 성공 후)
    @RequestMapping(value = "/game", method = RequestMethod.GET)
    public String game() {
        return "game";
    }
}

 

private static final Logger logger = LoggerFactory.getLogger(RootController.class);

logger.info → syso 는 단순 문자열출력으로 확인만 했다면 log 가 더 자세하게 오류 원인을 알려준다고 해서 사용해보았다. (프로젝트 만들면 기본으로 homecontroller에 샘플코드로 찍히는데 log 관련된 것 같길래 구글링해보니 이렇게 사용하면 된다고 해서 사용해본 것)

 

signup.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>dinogame</title>
</head>
<body>
<h2>회원가입</h2>
    <form action="${pageContext.request.contextPath}/signup" method="post">
        <div>
            <label for="userid">ID:</label>
            <input type="text" id="userid" name="userid" required>
        </div>
        <div>
            <label for="password">Password:</label>
            <input type="password" id="password" name="password" required>
        </div>
        <div>
            <button type="submit">Sign Up</button>
        </div>
    </form>
    <p>이미 회원이신가요? <a href="${pageContext.request.contextPath}/login">로그인</a></p>
</body>
</html>

required 속성 → html5 표준, 필드의 폼을 비워두고 제출하면 브라우저가 자동으로 해달 필드를 강조해 메세지를 표시한다 (유효성 검사와는 조금 다르고 빈 값이 없게 해주는 역할)

 

servlet-context

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.dino.root" />
	
	
	
</beans:beans>

두번째 수정)

전체 코드는 0803(토) 자정 쯤 조 단톡방에 보내드렸습니다.

 

  • 파일 구조

 

🛠 수정 사항

  • .do 로 매핑 → localhost:9999/login.do 이런 식으로 들어가야 한다.
  • home.jsp 파일의 이름을 명시성을 위해 game.jsp 파일로 이름 수정
  • 회원가입 기능 수행을 위해 MemberDAO, MemberVO, MemberService 클래스를 만들었다. (RootController에서 처리한 기능 분산)
  • MemberDAO에 DB 연결 코드 작성

 

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>
	
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

 

☄️ 공룡게임 전체코드

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

canvas.width = window.innerWidth - 100;
canvas.height = window.innerHeight - 100;

var dinoImg = new Image();
dinoImg.src = '../img/dino.png'; // 공룡 이미지 경로

var cactusImg = new Image();
cactusImg.src = '../img/cactus.png'; // 선인장 이미지 경로

let dino = {
    x: 10,
    y: 230,
    width: 60,
    height: 80,
    draw() {
        ctx.drawImage(dinoImg, this.x, this.y, this.width, this.height);
    }
};

class Cactus {
    constructor() {
        this.x = 500;
        this.y = 230;
        this.width = 20;
        this.height = 40;
    }
    draw() {
        ctx.drawImage(cactusImg, this.x, this.y, this.width, this.height);
    }
}

var timer = 0;
var cactusArr = [];
var jumpTimer = 0;
var animation;
var jumping = false;
var score = 0;

function frame() {
    animation = requestAnimationFrame(frame);
    timer++;

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    if (Math.random() < 0.01) {
        var cactus = new Cactus();
        cactusArr.push(cactus);
    }

    cactusArr.forEach((a, i, o) => {
        a.x -= 1; // 선인장 이동 속도

        if (a.x < 0) {
            o.splice(i, 1);
            score++; // 선인장이 지나갈 때 점수 증가
        }

        collision(dino, a);
        a.draw();
    });

    if (jumping) {
        dino.y -= 4; // 점프 상승 속도
        jumpTimer++;
    }

    if (jumpTimer > 20) {
        jumping = false;
        jumpTimer = 0;
    }

    if (!jumping && dino.y < 230) {
        dino.y += 1; // 점프 하강 속도
    }

    dino.draw();
    drawScore(); // 점수판 그리기
}
frame();

function collision(dino, cactus) {
    var xSubtraction = cactus.x - (dino.x + dino.width);
    var ySubtraction = cactus.y - (dino.y + dino.height);
    if (xSubtraction < 0 && ySubtraction < 0) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        cancelAnimationFrame(animation);
        showGameOver();
    }
}

var jumpSwitch = false;
let lastSpacePressTime = 0;

document.addEventListener('keydown', function(e) {
    if (e.code === 'Space') {
        const currentTime = Date.now();
        const timeSinceLastPress = currentTime - lastSpacePressTime;

        if (timeSinceLastPress > 500) {
            jumping = true;
            lastSpacePressTime = currentTime;
        }
    }
});

function drawScore() {
    ctx.font = "20px Arial";
    ctx.fillStyle = "black";
    ctx.fillText("Score: " + score, 10, 20);
}

function showGameOver() {
    ctx.font = "40px Arial";
    ctx.fillStyle = "black";
    ctx.fillText("Game Over", canvas.width / 2 - 100, canvas.height / 2);
    createRestartButton();
}

function createRestartButton() {
    var button = document.createElement("button");
    button.innerHTML = "Restart Game";
    button.style.position = "absolute";
    button.style.top = canvas.height / 2 + "px";
    button.style.left = canvas.width / 2 - 40 + "px";
    button.addEventListener("click", restartGame);
    document.body.appendChild(button);
}

function restartGame() {
    document.querySelector("button").remove();
    cactusArr = [];
    score = 0;
    dino.y = 230;
    frame();
}

 


0804 

이슈와 해결, 그리고 참고

  • 부트스트랩 라이브러리에 대한 이해 부족으로 생겼던 이슈

부트스트랩을 사용하면 클래스이름으로 부트스트랩 라이브러리에 값 인식을 하기 때문에 속성을 바꿀 때는 아이디값을 줘야하는데 임의로 클래스 이름을 바꿔 배열이 클어졌다. (왼)

클래스 이름을 원복하고 아이디 값을 줘 해결. (오른쪽)

 

그 밖에 팀원들과 참고한 블로그들)

 

스프링(Spring)에서 자바스크립트(JavaScript) 사용 방법

스프링에서 css, js, image 등의 자원은 파일이 존재하는 url 그 자체로 사용된다. 따라서 url 요청을 해야 하는데 이는 MVC의 DispatcherServlet에서 판단해 Controller에서 RequestMapping Annotation을 검색하게 된

minwoohi.tistory.com

 

 

 

[Spring] 컨트롤러와 프론트 사이에서 값 전달하는 방법의 종류

[1] 들어가며 Spring 프레임워크의 컨트롤러와 JSP사이에서 값을 주고 받는 것은 웹프로그래밍에서 가장 기본적인 작업입니다. 하지만 값을 전달하는 방법의 종류가 많고 프레임워크특성상 많은

admm.tistory.com


0805

우여곡절 끝에 커밋하고 깃에 올리고 발표까지 완료! 

+ Recent posts