클라우드 10일차 : mini project(페어)

soso·2024년 6월 21일

클라우드 부트캠프

목록 보기
12/77

https://github.com/sssoeun/realty

EJS의 JSON 값을 JavaScript로 전달하는 방법

server.js

 app.get('/realtycontent/:_id', (req, res) => { 
        mydb.collection('building')
            .findOne({_id:new ObjId(req.params._id)})       //req.body가 없기 때문에 사용 불가(get방식에서는 body가 공백)
            .then(result => {
                res.render('realtycontent.ejs', {data:result});
            })
            .catch((err) => {
                // console.log(err);
                res.status(500).send();
            });
    });

{data:result} JSON 값을 가지고 realtycontent.ejs에 랜더링

realtycontent.ejs

<input type="hidden" id="data" value='<%=JSON.stringify(data)%>'>

hidden input 태그로 JSON 형식의 data를 stringify()로 JSON 문자열로 바꿔 value 속성에 저장

<script>
    var jsonData = document.getElementById("data").value;
    var data = JSON.parse(jsonData);
    ...
</script>

script 태그 안에서 id값으로 불러온 후, value를 추출해 jsonData 변수로 저장
jsonData를 파싱(JavaScript 객체로 변환)하여 data라는 변수로 선언하면

var sumAddress = data.address + data.extraAddress;

이런 식으로 data의 속성값을 불러와서 쓸 수 있음

CSS 오류


모든 페이지에 템플릿을 적용했지만 특정 페이지의 css가 망가지는 경우 그 페이지의 개발자 도구(F12)창의 콘솔을 확인했을 때 이런 오류가 뜬다면 해당 파일의 경로를 제대로 설정해주면 된다

<link href="assets/vendor/animate.css/animate.min.css" rel="stylesheet">
<link href="../assets/vendor/animate.css/animate.min.css" rel="stylesheet">

몽고DB 오류


MongoServerSelectionError: 3C6C0000:error:0A000438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:c:\ws\deps\openssl\openssl\ssl\record\rec_layer_s3.c:1590:SSL alert number 80
몽고DB에 등록되지 않은 ip면 에러가 뜬다고 한다(인터넷이 끊겨서 핫스팟 연결하니 이 에러뜸)
다시 원래 쓰던 ip로 접속하면 해결됨

카카오맵 API geocoder 오류

위의 ejs의 JSON 안의 주소 값을 JavaScript로 받아온 후 카카오맵 API 기능 중 하나인 주소를 좌표값으로 변환하는 기능을 사용해 해당 주소의 위치를 지도로 표시하려 했다

하지만 Geocoder을 불러오지 못한다는 오류가 뜨면서 지도가 뜨지 않거나 카카오맵 기본 위치만 뜨는 문제가 발생하였다
해결 방법은 가이드에 나온 대로 APIKEY를 불러온 스크립트 링크 뒤에 파라미터를 붙이는 것이었다

아니면 이런 식으로 기존 APIKEY를 불러온 스크립트 위에 링크 뒤에 사용할 라이브러리의 파라미터를 붙인 스크립트를 위치하면 해결되었다 아래로 가면 적용이 안된다

<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=APIKEY&libraries=services"></script>
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=APIKEY"></script>
  

부트스트랩 dropdown 적용 안되는 문제

head 부분에 아래 script 추가

<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script>

참고
https://pika-chu.tistory.com/789

new Date().toISOString() 시차 해결

날짜의 포맷을 맞춰주는 toISOString()을 쓰면 UTC 시간대로 반환되는 문제

getTimezoneOffset()는 로컬 시간대과 UTC 시간대와의 차이를 분 단위로 반환하기 때문에 기존 밀리초 단위로 인자를 받는 new Date() 함수에 넣기 위해서 1000(밀리초)*60(초)를 곱해 밀리초 단위로 만듦

이후 현재 시간과의 차이만큼 빼어 시간을 설정

const offset = new Date().getTimezoneOffset() * 60000;
const today = new Date(Date.now() - offset).toISOString().split('T')[0];	//2017-03-11T02:30:00.000Z 형태로 반환하기 때문에 T 기준으로 문자열을 절삭

참고
https://velog.io/@zad1264/Date.toISOString-%EC%98%A4%ED%94%84%EC%85%8B-%EB%A7%9E%EC%B6%94%EA%B8%B0

Moment Timezone 라이브러리도 사용 가능

document.addEventListener('DOMContentLoaded', () => {
    document.querySelector('#25p').addEventListener('click', async (event) => {
        event.preventDefault();
        let data = await fetchData('25p');
        renderTable(data);
    });    
    document.querySelector('#30p').addEventListener('click', async (event) => {
        event.preventDefault();
        let data = await fetchData('30p');
        renderTable(data);
    });
    document.querySelector('#35p').addEventListener('click', async (event) => {
        event.preventDefault();
        let data = await fetchData('35p');
        renderTable(data);
    });
    ...

이렇게 일일이 다 쓸 필요없이 축약 가능

document.addEventListener('DOMContentLoaded', () => {
    const filterOptions = ['25p', '30p', ... ];

    filterOptions.forEach(option => {
        document.querySelector(`#${option}`).addEventListener('click', async (event) => {
            event.preventDefault();
            let data = await fetchData(option);
            renderTable(data);
        });
    });
});

id속성이 숫자로 시작하는 경우 선택자가 유효하지 않아
Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#25p' is not a valid selector.
이런 오류가 뜨면서 모든 드롭다운 버튼이 먹히지 않았다

querySelector가 유효하지 않은 선택자를 하나라도 만나면 JavaScript 코드가 중단되기 때문에 선택자를 올바르게 수정하여 해결

document.addEventListener('DOMContentLoaded', () => {
    const filterOptions = ['p25', 'p30', ... ];

    filterOptions.forEach(option => {
        document.querySelector(`#${option}`).addEventListener('click', async (event) => {
            event.preventDefault();
            let data = await fetchData(option);
            renderTable(data);
        });
    });
});

몽고DB 고유번호 지정해주기

몽고DB에서 입력값의 고유번호를 저장할 collection count을 만들어 필드에 0을 저장한 후(저장할 필드 데이터 타입을 Int32로 변경),
server.js에서 입력값을 저장할때마다 count를 1 더하는 방식으로 고유번호 설정

필드 데이터 타입을 설정 안해주면 server.js에서 직접 parseInt로 필드 데이터 타입을 string에서 int로 변경해주어야 한다

app.post('/save', async (req, res) => {
        const price = parseInt(req.body.price);
        console.log(req.body);
    
        try {
            const counter = await mydb.collection('counter').findOne({ name: '게시물갯수' });
            const totalPost = counter.totalPost;
    
            const result = await mydb.collection('building').insertOne({
                num: totalPost + 1,
                block: req.body.block,
                unit: req.body.unit,
                address: req.body.address,
                extraAddress: req.body.extraAddress,
                area: req.body.area,
                housing_type: req.body.housing_type,
                price,
                date: req.body.date,
                status: req.body.status
            });
    
            await mydb.collection('counter').updateOne(
                { name: '게시물갯수' }, { $inc: { totalPost: 1 }
            });
    
            console.log('저장 완료', result);
            res.redirect('/realtylist');
        } catch (err) {
            console.error('저장 실패', err);
            res.status(500).send('저장 실패');
        }
    });

참고
https://velog.io/@bbbgoat/4%EC%9D%BC%EC%B0%A8-Node.js-MongoDB-Part-2-%EA%B2%8C%EC%8B%9C%EB%AC%BC%EB%A7%88%EB%8B%A4-%EA%B3%A0%EC%9C%A0%EB%B2%88%ED%98%B8%EB%A5%BC-%EB%8B%AC%EC%95%84-%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0-DB-Update-%ED%95%A8%EC%88%98%EC%99%80-inc-%EC%97%B0%EC%82%B0%EC%9E%90-AJAX%EB%A1%9C-%EC%82%AD%EC%A0%9C%EC%9A%94%EC%B2%AD%ED%95%98%EA%B8%B0-HTML-%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%84%B1-%EC%84%9C%EB%B2%84-jQuery%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-UI-%EA%B8%B0%EB%8A%A5-%EC%97%AC%EB%9F%AC%EA%B0%80%EC%A7%80-%EC%9D%91%EB%8B%B5%EB%B0%A9%EB%B2%95

몽고DB 페이지네이션

옵션별로 정렬하는 드롭다운 버튼을 구현한 후, skiplimit를 이용하여 페이징 처리를 하다가 몽고DB의 _id값을 이용하면 모든 데이터를 읽어올 필요없이 서버에서 설정한 개수만큼만의 데이터만 한 페이지에 읽어오고, 마지막 데이터의 _id값을 추출해 그 기준으로 다음 페이지의 시작 데이터를 설정할 수 있다는걸 알아서 구현하려고 했다
하지만 _id값을 이용하는건 기본적으로 생성된 시간 순서대로만 정렬하는거고, 다양한 정렬을 사용하려면 모든 데이터를 읽어온 후 원하는 옵션으로 재정렬 한 후 skiplimit를 이용하여 페이지별로 출력해야한다

0개의 댓글