반응형

예전에 학원을다니면서 팀플로 만들었던 프로젝트가있는데

한동안 신경을 안썻더니 aws 에 올려놨던 데이터베이스가 계정이 폭파되면서 함께 없어졌다.. (이메일 체크를잘하자..)

다시 살려보려고 백엔드 부분 코드를 봤더니 굉장히 단순하고 왜 이렇게 했을까 싶은 부분들이 많았다.

그래서 Java로 다시 만들고 만드는김에 필요한 기능들을 조금 더 추가 해보고 DB 구조도 다시 만들어보기로 했다.

그렇게 오래 걸릴것같진않은데 클라이언트부분도 너무 예전에 만든코드라서 읽어보면서 하려면 머리는 조금 아플것같다. 

 

node.js 서버코드 

//common js 구문 import ---> require("모듈")
//express

const express = require("express");
const cors = require("cors");
const multer = require("multer")
const mysql = require("mysql");
const bcrypt = require('bcrypt'); //암호화 API
const saltRounds = 10;

//서버 생성
const app = express();

//포트번호
const port = 8080;

//브라우져의 cors이슈를 막기 위해 설정
app.use(cors());

// json형식 데이터를 처리하도록 설정
app.use(express.json());
// upload폴더 클라이언트에서 접근 가능하도록 설정
app.use("/upload",express.static("upload"));
//storage생성
const storage = multer.diskStorage({
    destination: (req,file,cb)=>{
        cb(null,'upload/')
    },
    filename:(req,file,cb)=>{
        const newFilename = file.originalname
        cb(null,newFilename)
    }
})
//upload객체 생성하기
const upload = multer({ storage : storage });
//upload경로로 post 요청시 응답 구현하기
app.post("/upload",upload.single("file"),async (req,res)=>{
    res.send({
        imageURL:req.file.filename
    })
})

//mysql 연결 생성
const conn = mysql.createConnection({
    host:process.env.AWS_ACCESS_HOST,
    user:"admin",
    password:process.env.AWS_ACCESS_KEY,
    port:"3306",
    database:"TeamProject"
})
conn.connect();

// conn.query("쿼리문","콜백함수")

app.get('/place',(req,res)=>{
    conn.query("select * from City",(error,result,field)=>{
        if(error){
            res.send(error)
        }else{
            res.send(result)
        }
    })
})
//http://localhost:8080/special/1
//req{ params: {no:1}}
// 나라정보 받아오기
app.get("/place/:place",(req,res)=>{
    const {place} =req.params;
    conn.query(`select * from City where cityname = "${place}"`,(err,result,field)=>{
        if(err){
            res.send(err)
        }else{
            res.send(result)
        }
    })
})

//나라별 마커 포인트 받아오기
app.get("/marker/:place",(req,res)=>{
    const {place} =req.params;
    conn.query(`select * from SpotPlace where Nation = "${place}"`,(err,result,field)=>{
        if(err){
            res.send(err)
        }else{
            res.send(result)
        }
    })
})

//회원가입요청
app.post("/join",async (req,res)=>{
    //입력받은 비밀번호를 mytextpass로 저장
    const mytextpass = req.body.m_pass;
    let myPass = ""
    const {m_name,m_nickname,m_email} = req.body;

    // 빈문자열이 아니고 undefined가 아닐때
    if(mytextpass != '' && mytextpass != undefined){
        bcrypt.genSalt(saltRounds, function(err, salt) {
            //hash메소드 호출되면 인자로 넣어준 비밀번호를 암호화 하여 콜백함수 안 hash로 돌려준다
            bcrypt.hash(mytextpass, salt, function(err, hash) {// hash는 암호화시켜서 리턴되는값.
                // Store hash in your password DB.
                myPass = hash;
                conn.query(`insert into member(m_name,m_pass,m_email,m_nickname) 
                values( '${m_name}' , '${myPass}' , '${m_email}' , '${m_nickname}')`
                ,(err,result,fields)=>{
                    console.log(result);
                    res.send("등록되었습니다.")
                })
            
            });
        });
    }
    console.log(req.body)
})
//로그인 요청 하기 
app.post("/login",async(req,res)=> {
    // 1.) useremail 값에 일치하는 데이터가 있는지 확인한다.
    // 2.) userpass 암호화해서 쿼리 결과의 패스워드랑 일치하는지 체크
    //{"useremail":"ㅁㄴㅇ""userpass":"asd"}
    const {useremail,userpass }=req.body;
    conn.query(`select * from member where m_email = '${useremail}'`, (err,result,fields)=>{
        //결과가 undefined 가 아니고 결과의 0번째가 undefined가 아닐때= 결과가 있을때 
        if(result != undefined && result[0] !=undefined ){
            bcrypt.compare(userpass,result[0].m_pass, function(err,rese){
                //result == true
                if(result){
                    console.log("로그인 성공");
                    res.send(result);
                }else{
                    console.log("로그인 실패");
                    res.send(result);
                }
            })
        }else{
            console.log("데이터가 존재하지 않습니다.");
        }
    })
})

//비밀번호찾기 
app.post("/findPass", async (req,res) => {
    const {useremail} = req.body;
    console.log(req.body)
    conn.query(`select * from member where m_email = '${useremail}'`,(err,result,field)=>{
        if(result){
            console.log(`결과${result[0].useremail}`)
            res.send(result[0].m_email)
        }else{
            console.log(err)
        }
    })
})

//패스워드 변경 요청
app.patch("/updatePass",async (req, res)=>{
    console.log(req.body)
    const {m_pass,m_email} = req.body;
    //update 테이블이름 set 필드이름= 데이터값 where 조건
        const mytextpass = m_pass;
    let myPass = ""

    if(mytextpass != '' && mytextpass != undefined){
        bcrypt.genSalt(saltRounds, function(err, salt) {
            //hash메소드 호출되면 인자로 넣어준 비밀번호를 암호화 하여 콜백함수 안 hash로 돌려준다
            bcrypt.hash(mytextpass, salt, function(err, hash) {// hash는 암호화시켜서 리턴되는값.
                // Store hash in your password DB.
                myPass = hash;
                conn.query(`update member set m_pass='${myPass}' where m_email='${m_email}'`
                ,(err,result,fields)=>{
                    if(result){
                        res.send("등록되었습니다.")
                    }
                    console.log(err)
                })
            
            });
        });
    }
})

//추천 관광지 받아오기
app.get("/recommend/:place",(req,res)=>{
    const {place} =req.params;
    conn.query(`select * from SpotPlace where Nation = "${place}" and recommend IS NOT NULL order by recommend`,(err,result,field)=>{
        if(err){
            res.send(err)
            console.log(err)
        }else{
            res.send(result)
            console.log(result)
        }
    })
})

app.get("/citydesc/:place",(req,res)=>{ //axios.get('/citydesc/${places}')
    const {place} = req.params;
    conn.query(`select month_1,month_2,month_3,month_4,month_5,month_6,month_7,month_8,month_9,month_10,month_11,month_12 from City where cityname = '${place}'`,
    (err,result,fields)=>{
        if(result){
            res.send(result)
            console.log(result)
        }
        console.log(err)
    })
})





app.listen(port,()=>{
    console.log("서버가 구동중입니다.")
})

 

이제 이 코드를 베이스로 다시 Db 구조를 짜고 Spring 으로 api 를 만들어 주자

쿼리를 왜 저렇게 만들었나 싶은데 * 로 모든 필드를 가져오도록 써놔서

테이블에 어떤 필드들이있었는지 알수없는게 몇개가 있다.

테이블 이름이랑 기능이 매치가 되지않는것도 몇개 있기도해서 테이블은 대대적인 재공사가 필요해 보인다..

 

유저를 정보를 이용해야하는 기능이 만들어져있지않아서 로그인기능에 필요한 유저테이블은 빼버렸다. 

추후에 로그인기능을 추가하고 유저정보를 이용하는 기능을 만들게 되면 필요해지겠지만

지금 현재의 기능에서는 이 테이블들만 이용된다.

 

관광지의 도시들을 추가하게되고 

각 도시들이 관광스팟을 여러개 가지고있게된다 

도시의 월별로 관광정보가 담겨있고 

도시를 기준으로 포스팅 url 도 담겨있게된다. 

 

city를 부모 테이블로해서 자식 테이블이 여러개 연결되어있다. = CityEntity 에 oneToMany로 연결해서 다 가져올수있다.

@Entity
@Table(name = "city")
public class CityEntity extends BaseEntity {
    private String name;
    private String coordinate;
    @OneToMany(mappedBy = "city", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<SpotPlaceEntity> spotPlaceList;

    @OneToMany(mappedBy = "city", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<CityMonthlyInfoEntity> cityMonthlyInfoEntities;

    @OneToMany(mappedBy = "city", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<RecommendPostsEntity> recommendPostsEntities;

    public Double getLat(){
        String[] split = coordinate.split(",");
        return Double.parseDouble(split[0]);
    }

    public Double getLon(){
        String[] split = coordinate.split(",");
        return Double.parseDouble(split[1]);
    }
}

 

CityEntity에 자식테이블들을 연결해서 모두 조회할수있도록 해주었다. 

프론트엔드에서 도시를 선택하고 들어가면 그 도시에 관련된정보들을 한페이지에서 모두 조회하기때문에.
한 엔티티로 모든 데이터를 다 조회할수있게 넘겨주는게 좋을것같았다.

 

나머지 엔티티들도 db구조에 맞춰서 구현해주고 컨트롤러를 만들어 주었다.

@RestController
@RequestMapping("place")
@RequiredArgsConstructor
public class CityController {
    private final CityBusiness cityBusiness;
    // 나라정보 받아오기 /place/:place
    @GetMapping("{placeName}")
    public Api<CityResponse> getPlace(@PathVariable("placeName") String placeName) {
        CityResponse city = cityBusiness.findCity(placeName);
        return Api.OK(city);
    }

    // 추천 관광지 받아오기 "/recommend/:place"
    @GetMapping("spot/{placeName}")
    public Api<List<SpotResponse>> getSpotPlaces(@PathVariable("placeName") String placeName) {
        List<SpotResponse> city = cityBusiness.findSpotList(placeName);
        return Api.OK(city);
    }

    // 나라의 월별정보
    @GetMapping("month/{placeName}")
    public Api<List<MonthInfoResponse>> getMonthInfo(@PathVariable String placeName){
        List<MonthInfoResponse> monthInfo = cityBusiness.findMonthInfo(placeName);
        return Api.OK(monthInfo);
    }
}

 

각 정보별로 메서드를 만들어줬다 도시이름을 파라미터로 받아서 해당도시를 db에서 조회하고 

도시에 관련된 각각의 정보들을 response로 컨버팅해서 내보내주게 된다.

 

 

여행추천 서비스 더 나들이 | Notion

사용한 기술 : React.js, Redux, SpringBoot3, MySql, GoogleMaps API, OpenWeatherMap API

humane-cut-ba5.notion.site

 

이렇게 프로젝트를 다시 살려보았다. 

그때 당시는 DB의 테이블에관한 개념도 별로없었고 테이블구조를어떻게 만들어야할지도 잘 몰랐어서

한 테이블에 1월 ~ 12월까지 필드를 만들어놓고 각 필드에 월 정보를 다 넣는식으로 만들고

city 테이블에 spot데이터도 모두 같이 들어있었던것같다.

다시 만들면서 보니까 그동안 공부한시간들이 조금 더 나은 구조를 만들수있게 도와준것같아서 기분이 좋았다.

반응형

+ Recent posts