먼저 배열에 N개의 수를 입력받고 이를 정렬한 후, 늘어나는 시간과 비교하며 M시간이 되면 빵을 K개만큼 추가하고, 만약 배열의 값을 cnt로 두고 배열[cnt] == 시간일 경우 빵의 개수가 0보다 같거나 작으면 불가, 크면 빵의 개수를 줄이고 cnt를 1씩 늘리고 time도 1 늘리는 방법으로 해결하려 하였다
2. 오류
그러나 배열내부에 배열[0] == 3 배열[1] == 3같이 같은 값이 들어가 있을 경우 시간은 이미 늘어나버려서 무한루프에 빠지는 현상이 발생하였고 while문을 2번 돌리는 방법으로 다음과 같이 해결하였다. 더 쉬운 방법도 존재하는 것 같으니 다른 블로그도 참고하길 바란다.
3. 코드
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
string result = "Possible";
int tmp[101];
int test_case;
int T;
int N;
int M;
int K;
void check(){
int time = 0;
int cnt = 0;
int bread = 0;
while(1){
if(cnt == N) {break;}
if((time % M) == 0 && time != 0) bread += K;
while(1){
if(tmp[cnt] == time){
if(bread <=0){
result = "Impossible";
goto EXIT;
}
else{
bread--;
cnt++;
}
}else{
break;
}
}
time++;
}
EXIT:
return;
}
int main(int argc, char** argv)
{
scanf("%d",&T);
for(test_case = 1; test_case <= T; ++test_case)
{
scanf("%d %d %d",&N ,&M ,&K);
for(int i = 0; i < N; i++){
scanf("%d", &tmp[i]);
}
sort(tmp, tmp+N);
result = "Possible";
check();
printf("#%d %s\n", test_case ,result.c_str());
}
return 0;//정상종료시 반드시 0을 리턴해야합니다.
}
다음과 같은 spring MVC 아키텍처에서 우리는 controller와 view부분만 만들면 되며 나머지 부분은 스프링에서 제공한다. 사용자의 request가 들어올 경우 dispatcher servlet가 요청을 가로채고 handler Mapping에 전달한 후 컨트롤러로 요청이 전달하고 우리가 만든 컨트롤러에 의해 view 네임을 선택하면 view Resolver에 의해 view네임을 가진 jsp를 선택하게 된다.
2. 스프링 MVC 실습하기
사전준비
다음과 같이 pom.xml에 필요한 라이브러리를 추가한다. 게터, 세터를 용이하게 만들어주는 lombok mysql과 연동하기 위한 dbcp, mysql-connector, spring-jdbc를 추가한다. 다음과 같은 deoendency를 추가한다
다음과 같이 Spring Legacy Project의 Spring MVC Project를 클릭하고 helloSpringMVC라는 프로젝트를 생성한다
#2) home.jsp 수정
다음과 같이 기본으로 있는 home.jsp를 수정한다. 클릭할 시 createoffer로, offers로 요청을 보내는 a태그가 2개 있다. 이때 ${pageContext.request.contextPath}는 서버에서 설정된 contextPath를 의미한다.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<p> <a href="${pageContext.request.contextPath}/offers">show current offers</a>
<p> <a href="${pageContext.request.contextPath}/createoffer">Add a new offer</a>
</body>
</html>
#3) OffersController 생성하기
다음과 같은 OffersController를 생성한다.
package kr.ac.hansung.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import kr.ac.hansung.model.Offer;
import kr.ac.hansung.service.OffersService;
@Controller
public class OffersController {
@RequestMapping("/offers")
public String showOffers(Model model) {
return "offers";
}
@RequestMapping("/createoffer")
public String createOffer(Model model) {
return "createoffer";
}
}
#4) offers.jsp createoffers.jsp생성하기
jsp코드는 이후에 수정될 예정이니 controlle가 jsp로 제대로 요청을 전달하는지 확인만 할 목적으로 기본 jsp2개를 생성한다
#5) 실행하기
다음과 같이 제대로 매핑이 된 것을 확인할 수 있다.
#6) dao, model, service 생성하기
다음 그림과 같은 구조를 생성하기 위해 dao, model, service를 생성한다. 사용자의 요청이 들어오면 Dispatcher Servlet에서 이를 컨트롤러에 전달해주고 컨트롤러는 이에 맞는 서비스에게 전달하고 서비스는 DAO에게 DAO는 DB에 접근하고 모델을 통해 View로 사용자에게 response 하는 구조이다
OfferDAO
처음 프로젝트 생성할 때 패키 지명을 kr.ac.hansung으로 지정했기 때문에 DAO의 패키지명을 kr.ac.hansung.dao로 패키지를 만들고 내부에 다음과 같은 OfferDAO라는 클래스를 생성한다. @Repository 에노테이션을 통해 DAO클래스임을 명시하고 @Autowired를 통해 DB에 접근할 jdbctemplet을 의존성 주입시킨다.
package kr.ac.hansung.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import kr.ac.hansung.model.Offer;
@Repository
public class OfferDAO {
private JdbcTemplate jdbcTemplateObject;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
public int getRowCount() {
String sqlStatement = "select count(*) from offers";
return jdbcTemplateObject.queryForObject(sqlStatement, Integer.class);
}
public Offer getOffer(String name) {
String sqlStatement = "select * from offers where name=?";
return jdbcTemplateObject.queryForObject(sqlStatement, new Object[]{name}, new RowMapper<Offer>() {
@Override
public Offer mapRow(ResultSet rs, int rowNum) throws SQLException {
Offer offer = new Offer();
offer.setId(rs.getInt("id"));
offer.setName(rs.getString("name"));
offer.setEmail(rs.getString("email"));
offer.setText(rs.getString("text"));
return offer;
}
});
}
public List<Offer> getOffers() {
String sqlStatement = "select * from offers";
return jdbcTemplateObject.query(sqlStatement, new RowMapper<Offer>() {
@Override
public Offer mapRow(ResultSet rs, int rowNum) throws SQLException {
Offer offer = new Offer();
offer.setId(rs.getInt("id"));
offer.setName(rs.getString("name"));
offer.setEmail(rs.getString("email"));
offer.setText(rs.getString("text"));
return offer;
}
});
}
public boolean insert(Offer offer) {
String sqlStatement = "insert into offers (name, email, text) values (?,?,?)";
String name = offer.getName();
String email = offer.getEmail();
String text = offer.getText();
return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text}) == 1);
}
public boolean update(Offer offer) {
String sqlStatement = "update offers set name=?, email=?, text=? where id=?";
String name = offer.getName();
String email = offer.getEmail();
String text = offer.getText();
int id = offer.getId();
return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text, id}) == 1);
}
}
Offer
kr.ac.hansung.model 패키지를 생성하고 다음과 같은 Offer클래스를 생성한다. lombok의 게터 세터 에노테이션을 통하여 게터 세터를 만든다
package kr.ac.hansung.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Offer {
private int id;
private String name;
private String email;
private String text;
public Offer() {
}
public Offer(String name, String email, String text) {
this.name = name;
this.email = email;
this.text = text;
}
public Offer(int id, String name, String email, String text) {
this.id = id;
this.name = name;
this.email = email;
this.text = text;
}
@Override
public String toString() {
return "Offer [id=" + id + ", name=" + name + ", email=" + email + ", text=" + text + "]";
}
}
OfferService
kr.ac.hansung.service패키지를 생성하고 다음과 같은 OfferService 클래스를 생성한다. Service에노테이션을통해 이클래스가 서비스임을 명시하고 Autowired를 통해 DAO객체를 의존성 주입시킨다.
package kr.ac.hansung.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.ac.hansung.dao.OfferDAO;
import kr.ac.hansung.model.Offer;
@Service
public class OffersService {
private OfferDAO offersDao;
@Autowired
public void setOffersDao(OfferDAO offersDao) {
this.offersDao = offersDao;
}
public List<Offer> getCurrent() {
// TODO Auto-generated method stub
return offersDao.getOffers();
}
}
#7) service-context.xml dao-context.xml 생성하기
기존의 HomeController가 작동할 수 있었던 이유는 web.xml을 보면 알 수 있다. 먼저 에노테이션이 활성화되어있고 ViewResolver를 사용하여 jsp의 이름을 받을 경우 name.jsp로 변경하여 연결해주는 것을 확인할 수 있다. 또한 base-package가 정해져 있어 패키지의 에노테이션을 확인하는것을 알 수 있다. 이처럼 service와 dao의 bean을 생성하고 autowired해주기위해 service-context.xml 와 dao-context.xml을 생성한다.
먼저 src/main/webapp/WEB-INF/spring/appServlet에 다음과 같은 service-context.xml을 생성한다. kr.ac.hansung.service패키지를 스캔하도록 되어있으며 에노테이션을 사용한다고 적혀있다.
다음으로는 service-context.xml과 같은 위치인 src/main/webapp/WEB-INF/spring/appServlet에 다음과 같은 dao-context.xml을 생성한다. dao-context에는 dbcp를 활용하여 DB에 접근하게 할 dataSource Bean이 정의되어있다. 또한 placeholder로 dbcp에서 참고할 properties파일의 위치가 정의되어있다.
src/main/webapp/WEB-INF에 props라는 폴더를 생성하고 다음과 같은 jdbc.properties 파일을 생성한다. 이는 dao-context.xml에서 dbcp를 이용하여 db에 접근할 때 사용하는 파일으로서 다음과 같다.
jdbc.username = 아이디를 입력
jdbc.password = 비밀번호를 입력
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/csemall
#9) web.xml 수정하기
이렇게 dao와 service를 생성하였어도 실행할 경우 에러가 발생한다. 이는 스프링에서 dao-context.xml와 service-context.xml을 바라보고 있지 않기 때문이다. 따라서 다음과 같이 web.xml을 수정한다. 기존에는 root-context.xml만 바라보고 있었지만 생성한 service-context.xml과 dao-context.xml을 추가해줌으로써 스프링이 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 http://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
/WEB-INF/spring/appServlet/service-context.xml
/WEB-INF/spring/appServlet/dao-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>
#10) offers.jsp수정
DB에 존재하는 정보를 받아오는 jsp를 구성하기 위해 다음과 같이 offers.jsp를 수정한다.
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Insert title here</title>
</head>
<body>
<c:forEach var="offer" items="${offers}">
<p>
<c:out value="${offer}">
</c:out>
</p>
</c:forEach>
</body>
</html>
1. 사용자는 /으로 접속하게 되면 HomeController에 의해 home.jsp로 접속하게 된다.
2. 사용자가 offers로 요청을 보내게 되면 디스패쳐 서블릿에 의해 OffersController로 요청을 전달하고, /offers로 매핑된 showOffers함수를 호출한다. showOffers는 autowired 된 offerService의 getCurrent() 함수를 호출한다.
3. offerService의 getCurrent함수는 autowried 된 offerDAO의 getOffers를 호출한다
4. offerDAO의 getOffers는 DB에 저장된 객체를 리스트로 저장하여 서비스로 반환하고 서비스는 다시 컨트롤러로 반환한다.
5. 컨트롤러에서는 모델을 이용하여 view에 있는 jsp로 결과값과 함께 반환하고, 사용자는 마지막으로 반환된 offers.jsp를 확인하게 된다.
먼저 NXN Array를 지뢰 = -1 나머지 칸은 숫자로 바꾼 Change_Array를 만들고 바꾼 Change_Array의 값이 0이고, 방문 안 한 곳을 체크하게 하고, 문제 의도대로 8방향으로 dfs 하였다. 이후 값이 0과 -1 이아닌 값들 중 방문하지 않은 개수만큼 개수를 추가해주는 방법으로 해결하였다
2. 코드
코드는 다음과 같다
#include<iostream>
#include<string.h>
using namespace std;
int T;
int N;
char array[300][300];
int change_array[300][300];
bool chk_array[300][300];
int count = 0;
int dx[8] = {-1, 0, 1, -1, 1, -1, 0 ,1};
int dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
void change(){
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
if(array[i][j] == '*') change_array[i][j] = -1;
else{
int cnt = 0;
for(int k = 0; k < 8; k++){
int nx = i + dx[k];
int ny = j + dy[k];
if(nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
if(array[nx][ny] == '*') cnt++;
}
change_array[i][j] = cnt;
}
}
}
}
void dfs(int x, int y){
chk_array[x][y] = true;
for(int k = 0; k < 8; k++){
int nx = x + dx[k];
int ny = y + dy[k];
if(nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
else{
if(change_array[nx][ny] == 0 && !chk_array[nx][ny]){
dfs(nx, ny);
}else{
chk_array[nx][ny] = true;
}
}
}
}
int main(int argc, char** argv)
{
cin >> T;
for(int i = 0; i < T; i++){
count = 0;
memset(array, 0, sizeof(array));
memset(change_array, 0, sizeof(change_array));
memset(chk_array, 0, sizeof(chk_array));
cin >> N;
for(int j = 0; j < N; j++){
for(int k = 0; k < N; k++){
cin >> array[j][k];
}
}
change();
for(int j = 0; j < N; j++){
for(int k = 0; k < N; k++){
if(change_array[j][k] == 0 && !chk_array[j][k]){
count++;
dfs(j,k);
}
}
}
for(int j = 0; j < N; j++){
for(int k = 0; k < N; k++){
if(change_array[j][k] != 0 && !chk_array[j][k] && change_array[j][k] != -1 ){
count ++;
}
}
}
cout << "#" <<i+1<<" "<<count<<endl;
}
}
다음과 같이 express-session과 session-file-store를 설치한다. express-session은 세션을 사용하기 위한 미들웨어이고, session-file-store는 session을 파일로 저장하기 위해 사용하는 미들웨어이다. 만약 DB에 저장하려면 다른 미들웨어를 사용하면 된다.
다음과 같이 사용자가 접속할 경우 session.num을 1로 추가하여 1씩 증가하는 코드이다. 만약 이 코드를 세션 스토어를 사용하지 않고 사용했을 경우에는 node서버를 재시작할 경우 num이 다시 1부터 증가하는 것을 확인할 수 있지만 세션 스토어를 사용할 경우 프로젝트 내부에 session이라는 폴더가 생기고 그 안에 암호화된 세션 파일이 생섬 됨을 확인할 수 있다. 따라서 서버를 재시작하더라도 num이 초기화되지 않는 것을 확인할 수 있다. 이를 이용하면 서버를 재시작하여도 사용자의 로그인이 끊기지 않는다는 등 다양한 구현이 가능해진다.
지난 #8 강의에 이어 MYSQL과 연동하여 Update, delete, insert 하는 방법을 설명하도록 한다.
1. INSERT
먼저 지난번 query와 달리 update메소드를 사용하며 다음과 같이 사용한다 update메서드 내부에는 insert sql문, new Object로 name, email, text를? 에 넣어주는 방식으로 사용하며 이때 반환하는 값이 1이면 true를 리턴한다.
public boolean insert(Offer offer) {
String sqlStatement = "insert into offers (name, email, text) values (?,?,?)";
String name = offer.getName();
String email = offer.getEmail();
String text = offer.getText();
return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text}) == 1);
}
2. UPDATE
insert와 같이 update메소드를 사용하며 사용방법은 다음과 같다.
public boolean update(Offer offer) {
String sqlStatement = "update offers set name=?, email=?, text=? where id=?";
String name = offer.getName();
String email = offer.getEmail();
String text = offer.getText();
int id = offer.getId();
return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text, id}) == 1);
}
3. DELETE
마찬가지로 update 메소드를 사용하며 사용방법은 다음과 같다.
public boolean delete(int id) {
String sqlStatement = "delete from offers where id = ?";
return (jdbcTemplateObject.update(sqlStatement, new Object[]{id}) == 1);
}
DB에 접근하기 위해 다음 그림과 같이 DAO는 JDBC Template에 이는 JDBC Driver를 통해 Database에 접근하고 DataSource를 이용하여 DB에 실질적으로 커넥션 한다.
2. Data Source
데이터 베이스에 접근하기 위해 DataSource를 사용한다 DataSource의 종류는 다양하지만 DBCP라이브러리의 BasicDataSource를 통상 많이 사용하므로 실습에서도 이를 사용할 예정이다.
3. DAO란?
Data Access Object의 약자로서 서비스는 DB에 대해 아무것도 모르고, 실질적으로 DB에 접근하는 오브젝트를 의미한다. 쉽게 생각하여 DB의 CRUD를 실질적으로 수행하는 코드가 담긴 부분이다.
4. JDBC란?
JDBC란 DB와의 커넥션, SQL 사용, Close 커넥션 등 잡다한 일들을 해주는 프레임 워크이다. 기본 JAVA JDBC는 커넥션 등 try catch의 반복으로 반복적인 코드를 짜야하지만 Spring JDBC를 사용하면 SQL만 코드를 작성하고 나머지는 Spring 프레임워크에서 담당한다.
5. 실습하기
#1) 사전 준비
Mysql 5.7.28 설치
Mysql내부에는 csemall라는 DB, offers라는 테이블을 만들고 다음과 같은 구조로 이루어져 있다.
id(INT)
name
email
text
1
Alice
Alice@google.com
alice
2
Bob
Bob@google.com
bob
3
Jun
Jun@google.com
jun
#2) Simple Spring Maven 프로젝트 실행
다음과 같은 helloDB프로젝트를 생성한다.
#3) pom.xml 수정
다음과 같은 dependency를 추가하고 저장을 누르면 maven에 라이브러리가 추가된 것을 확인할 수 있다.
props라는 패키지를 생성하고 그 밑에 jdbc.properties라는 파일을 생성한다. 파일 내부는 다음과 같다 이는 DB의 정보를 담고 있으며, 생성할 bean에 하드 코딩하는 것보다 좋은 방법이다.
jdbc.username = root
jdbc.password = 사용자 비밀번호를 입력
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/csemall
#5) beans.xml 생성
다음과 같이 beans.xml을 생성한다 context:annotation은 DAO클래스에서 사용할 @Autowired를 활성화한다는 의미이며 context:component는 @Component 에노 테이션이 있는 어떤 패키지를 scan 할 건지 명시하는 부분이다. bean으로 생성한 dataSource는 #4에서 생성한 jdbc.properties를 사용하여 실제 DB와 커넥션을 하는 부분이다 dbcp2의 BasicDataSource클래스를 사용하고 있고 이 클래스는 DataSource인터페이스를 따르는 클래스이다. 마지막으로 context:property-placeholder는 앞서 사용할 jdbc.properties가 어떤 위치에 있는지 명시하는 부분이다.
src/main/java아래의 csemall이라는 패키지를 만들고 다음과 같은 Class를 생성한다. @Component 에노테이션은 이 클래스는 id가 offerDAO라는 bean을 하나 생성하겠다는 의미이다. 또한 JdbcTemplate를 사용하여 bean에 생성한 dataSource를 주입(@Autowired)시켜 DB와 실질적인 커넥션을 해주는 부분이 담겨있다. getRowCount() 부분은 다음과 같은 sql을 query 하고 결과는 Integer로 받는다는 의미이다.
package csemall;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component("offerDAO")
public class OfferDAO {
private JdbcTemplate jdbcTemplateObject; //얘는 new를 사용해됨 이후 DataSource는 DI를 통해 주입 시킬예정
@Autowired
public void setDataSource(DataSource dataSource) { //DataSource는 인터페이스인데 우리가 앞서 bean에 선언한 datasource가 이 인터페이스를 따르고 DI한다
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
public int getRowCount() {
String sqlStatement = "select count(*) from offers";
return jdbcTemplateObject.queryForObject(sqlStatement, Integer.class);
}
}
#6) Offer.java 클래스 생성
Lombok을 이용해 @Getter, @Setter를 생성하였고 DB에 저장된 id, name, email, text를 받는다. 이는 추후에 사용될 클래스이다.