안녕하세요! 오늘은 졸업프로젝트로 진행한 대학생 시간표 생성 프로그램, 커벨리오를 만들면서 제가 참여한 부분과 그 과정에서 느낀점등을 정리해보고자 합니다 :)
대학생을 대상으로 하는 시간표 생성 프로그램의 특성상 방대한 양의 데이터가 필요해서 DB를 사용하는 것이 필수적이었습니다. 그래서 저희는 관계형 데이터베이스중 많이 사용되는 MySQL을 연동하여 사용하기로 하였습니다.
환경
JDK 다운로드
JDBC 파일 추가
java 프로젝트 생성
프로젝트 설정
참고
프로젝트가 진행되면서 java 코드는 다른 팀원이 전담하게 되어서 제가 작성한 부분에서 디버깅을 어떻게 하였는지를 중심으로 작성해보았습니다..!
Connection conn = null;
Statement state = null;
Statement state3 = null;
ResultSet resset = null;
ResultSet resset2 = null;
ResultSet resset3 = null;
PreparedStatement pstmt = null;
PreparedStatement pstmt2 = null;
PreparedStatement pstmt3 = null;
PreparedStatement pstmt4 = null;
//중략
sql = "select * from majors where recommend_time=32";
sql2 = "insert into graduate.time_table (table_number,week,period,course_name,division_number,professor_name) values(?,?,?,?,?,?)";
sql3 = "select * from user";
sql4 = "select * from user1_dropmajor";
pstmt = conn.prepareStatement(sql);
pstmt2 = conn.prepareStatement(sql2);
pstmt3 = conn.prepareStatement(sql3);
pstmt4 = conn.prepareStatement(sql4);
String major_name = "";
String this_time = "";
String professor_name = "";
resset = state.executeQuery(sql);
while (resset.next()) {
major_name = resset.getString("major_name");
table_name.add(major_name);
this_time = resset.getString("this_time");
table_time.add(this_time);
professor_name = resset.getString("professor_name");
table_professor.add(professor_name);
}resset.close();// close resultset
resset = state.executeQuery(sql3);
String Timeout = "";
while (resset.next()) {
Timeout = resset.getString("time_out");
out_time.add(Timeout);
}resset.close();// close resultset
resset = state.executeQuery(sql4);
String out_name = "";
String out_div = "";
String out_flag = "";
while (resset.next()) {
out_name = resset.getString("major_name");
name_out.add(out_name);
out_div = resset.getString("division_number");
div_out.add(out_div);
out_flag = resset.getString("flag");
flag_out.add(out_flag);
}
for (int m = 0; m < table_name.size(); m++) { // ArrayList 만큼 반복
if(table_name.get(m)!=null) {
if (duplicate_count.containsKey(table_name.get(m))){ // HashMap 내부에 이미 key 값이 존재하는지 확인
duplicate_count.put(table_name.get(m), duplicate_count.get(table_name.get(m)) + 1); // key가 이미 있다면 value에 +1
} else { // key값이 존재하지 않으면
duplicate_count.put(table_name.get(m), 1); // key 값을 생성후 value를 1로 초기화
}
}
}
public static int table_maker(String[] semi_table, int table2_number) {
int flag=0;
for (int i = 0; i < 6; i++) {
if (semi_table[i].length() == 2) {
day1 = (Integer.parseInt(semi_table[i].substring(0, 1)) - 1);
time1 = (Integer.parseInt(semi_table[i].substring(1, 2)) - 1);
for(int j=0;j<out_time.get(1).length();j++) {
if(j%2==0) {
if(day_out[j]==(day1+1)&&time_out[j+1]==(time1+1)) {
flag=1;
}
}
}
real_table[day1][time1][table2_number] = Integer.toString(i);
} else if (semi_table[i].length() == 4) {
day1 = (Integer.parseInt(semi_table[i].substring(0, 1)) - 1);
time1 = (Integer.parseInt(semi_table[i].substring(1, 2)) - 1);
day2 = (Integer.parseInt(semi_table[i].substring(2, 3)) - 1);
time2 = (Integer.parseInt(semi_table[i].substring(3, 4)) - 1);
for(int j=0;j<out_time.get(1).length();j++) {
if(j%2==0) {
if(day_out[j]==(day1+1)&&time_out[j+1]==(time1+1)) {
//System.out.println(day1+" "+time1);
flag=1;
}if(day_out[j]==(day2+1)&&time_out[j+1]==(time2+1)) {
//System.out.println(day2+" "+time2);
flag=1;
}
}
}
real_table[day1][time1][table2_number] = Integer.toString(i);
real_table[day2][time2][table2_number] = Integer.toString(i);
} else if (semi_table[i].length() == 6) {
day1 = (Integer.parseInt(semi_table[i].substring(0, 1)) - 1);
time1 = (Integer.parseInt(semi_table[i].substring(1, 2)) - 1);
day2 = (Integer.parseInt(semi_table[i].substring(2, 3)) - 1);
time2 = (Integer.parseInt(semi_table[i].substring(3, 4)) - 1);
day3 = (Integer.parseInt(semi_table[i].substring(4, 5)) - 1);
time3 = (Integer.parseInt(semi_table[i].substring(5, 6)) - 1);
for(int j=0;j<out_time.get(1).length();j++) {
if(j%2==0) {
if(day_out[j]==(day1+1)&&time_out[j+1]==(time1+1)) {
flag=1;
}if(day_out[j]==(day2+1)&&time_out[j+1]==(time2+1)) {
flag=1;
}if(day_out[j]==(day3+1)&&time_out[j+1]==(time3+1)) {
flag=1;
}
}
}
real_table[day1][time1][table2_number] = Integer.toString(i);
real_table[day2][time2][table2_number] = Integer.toString(i);
real_table[day3][time3][table2_number] = Integer.toString(i);
}
}return flag;
}
public static int blank_check(int index) {
int flag=0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 7; j++) {
if (real_table[i][j][index] != null) {
int n = Integer.parseInt(real_table[i][j][index]);
if(name_table[index][n]==null) {
flag=1;
break;
}
}
}
}
return flag;
}
String day = "";
String course_name = "";
String prof_name = "";
f=table_maker(created_table[0], 0);
int flag2=blank_check(0);
/*
System.out.println(f);
System.out.println(flag2);*/
if (f==0 && flag2==0) {
for (int i = 0; i < 5; i++) {
for (j = 0; j < 7; j++) {
if (real_table[i][j][0] != null) {
if (i == 0) {
day = "월";
} else if (i == 1) {
day = "화";
} else if (i == 2) {
day = "수";
} else if (i == 3) {
day = "목";
} else if (i == 4) {
day = "금";
}
int bun = 0;
int n = Integer.parseInt(real_table[i][j][0]);
course_name = name_table[0][n];
bun = bunban_table[0][n];
prof_name = pname_table[0][n];
pstmt2.setInt(1, 1);
pstmt2.setString(2, day);
pstmt2.setInt(3, j + 1);
pstmt2.setString(4, course_name);
pstmt2.setInt(5, bun);
pstmt2.setString(6, prof_name);
int r = pstmt2.executeUpdate();
}
}
}
}
IDE는 IntelliJ를 사용하였습니다
spring framework를 사용해보는 것이 처음이라 강의를 들으면서 시작해보았습니다.
강의에 따라 다운로드를 받기 위해 Spring initializer에 접속했습니다. 언어는 java를 사용하는 것을 선택하고 Maven과 gradle중에 강의를 따라 gradle을 선택하였는데 자료는 maven이 더 많은것 같아서 취향에 따라 선택하시면 될 것 같습니다.
java의 버전은 더 높은 버전만 아니면 일단 낮은 버전을 다운받고 gradle설정에서 변경하였습니다. Dependency는 기본적인 Spring Web과 Thymeleaf, java를 사용할 것이기 때문에 JPA도 추가해주었습니다.
plugins {
id 'org.springframework.boot' version '2.3.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.board'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
/src/main/java에 들어가면 폴더명+Application.java가 자동 생성 되어있고 초록색 화살표를 누르면 빌드+실행이 가능합니다.
오류없이 성공적으로 실행되었다면 http://localhost:8080/에 접속하여 다음과 같은 화면을 보실 수 있을겁니다.
spring에서 JPA를 사용한다면 코드는 크게 Controller, Model, Repository, Service의 4가지 부분으로 이루어집니다. 이름은 개발자마다 다를 수 있지만 기본적인 데이터의 틀을 선언하여 DB와 직접 연동되는 Model, 이 모델을 활용하여 DB에 쿼리를 날리고 Service에 그 값을 반환하는 Repository, Controller에서 받은 parameter 값이나 Repository에서 받은 return 값을 이용하여 실질적인 기능을 구현하는 Service, 마지막으로 프론트와 Spring을 연동해주며 Service의 함수들을 호출하는 Controller의 기본적인 틀은 일치할 것입니다.
package com.board.back.model;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
//유저테이블
@Entity
@Table(name = "User")
@DynamicInsert
@DynamicUpdate
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_no")
private Integer user_no;
// 아이디(학번)
@Column(name = "user_id")
private Integer user_id;
// 비밀번호
@Column(name = "user_pw")
private String user_pw;
// 전공
@Column(name = "user_major")
private Integer user_major;
// 학년
@Column(name = "user_grade")
private Integer user_grade;
public Integer getNo() {
return user_no;
}
public void setNo(Integer user_no) {
this.user_no = user_no;
}
public Integer getId() {
return user_id;
}
public void setId(Integer user_id) {
this.user_id = user_id;
}
public String getPassword() {
return user_pw;
}
public void setPassword(String user_pw) {
this.user_pw = user_pw;
}
public Integer getMajor() {
return user_major;
}
public void setMajor(Integer user_major) {
this.user_major = user_major;
}
public Integer getGrade() {
return user_grade;
}
public void setGrade(Integer user_grade) {
this.user_grade = user_grade;
}
public User(Integer user_id, String user_pw, Integer user_major, Integer user_grade) {
super();
this.user_id = user_id;
this.user_pw = user_pw;
this.user_major = user_major;
this.user_grade = user_grade;
}
@Override
public String toString() {
return "User [user_no=" + user_no + ", id=" + user_id + ", password=" + user_pw + ", major=" + user_major
+ ", grade=" + user_grade + "]";
}
}
@Column(name = "user_id")
private Integer user_id;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_no")
private Integer user_no;
package com.board.back.repository;
import com.board.back.model.Class;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ClassRepository extends JpaRepository<Class, Integer> {
@Query(value="SELECT * from Class s where course_id in :d", nativeQuery = true)
List<Class> printClass(@Param("d")List<Integer>d);
@Query(value="SELECT course_id from Class s where s.course_id in :d group by s.course_id", nativeQuery = true)
List<Integer> printClassCourseId(@Param("d")List<Integer>d);
@Query(value="SELECT class_time from Class s where s.class_no in :d", nativeQuery = true)
List<Integer> findCtime(@Param("d")List<Integer>d);
@Query(value="select course_id from Class s where s.class_no in :class_no",
nativeQuery = true)
List<Integer> findCCID(@Param("class_no")List<Integer>class_no);
}
package com.board.back.service;
import com.board.back.exception.ResourceNotFoundException;
import com.board.back.model.*;
import com.board.back.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private CourseRepository courseRepository;
@Autowired
private CheckFieldRepository checkFieldRepository;
@Autowired
private FieldRepository fieldRepository;
@Autowired
private UserCourseRepository UserCourseRepository;
@Autowired
private UserCheckFieldRepository userCheckFieldRepository;
@Autowired
private UserFieldRepository userFieldRepository;
public void createUser(User user) {
userRepository.save(user);
//생략
}
package com.board.back.controller;
import com.board.back.model.User;
import com.board.back.model.User2;
import com.board.back.repository.LoginRepository;
import com.board.back.service.LoginService;
import com.board.back.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@CrossOrigin(origins = "http://localhost:3000")
@RestController
@RequestMapping("/api")
public class UserController {
private LoginService loginService;
private UserService userService;
private LoginRepository loginRepository;
@Autowired
public UserController(UserService userService, LoginService loginService) {
this.userService = userService;
this.loginService = loginService;
}
@PostMapping("/login")
public void loginUser(@RequestBody User user) {
System.out.println("@PostMapping(\"/login\")");
System.out.println(user.toString());
loginService.postUser(user);
}
@GetMapping("/login2")
public int checkUser() {
if (loginService.getStatus() == true || loginService.getStatus() == false)
return loginService.getNo();
return loginService.getNo();
}
@PostMapping("/user")
public void createUser(@RequestBody User user) {
System.out.println("@PostMapping(\"/user\")");
System.out.println(user.toString());
userService.createUser(user);
}
}
@CrossOrigin(origins = "http://localhost:3000")
@RestController
@RequestMapping("/api")
private LoginService loginService;
private UserService userService;
private LoginRepository loginRepository;
@Autowired
public UserController(UserService userService, LoginService loginService) {
this.userService = userService;
this.loginService = loginService;
}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/DB명?serverTimezone=UTC&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=비밀번호
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://RDS주소:3306/graduate?serverTimezone=UTC&characterEncoding=UTF-8
spring.datasource.username=admin
spring.datasource.password=비밀번호
jpa:
database:mysql
generate-ddl:true
show-sql:true
개발 후반부터는 React를 사용한 프론트를 주로 담당하였습니다. 툴을 Visual Studio Code를 사용하였습니다.
react에는 다양한 기술들이 들어가는데 spring과의 연동을 중점으로 설명하겠습니다.
import axios from 'axios';
class UserService {
createUser(user) {
return axios.post("http://localhost:8080/api/user", user);
}
//생략
}
my2(user_no){
return axios({method :'post',
url:'http://localhost:8080/api/my2',
headers :{'Content-Type': 'application/json' },
data: user_no});
}
마지막으로 배포에 해당하는 호스팅 작업을 AWS를 이용하여 진행하였습니다.