프론트에서 주소를 받아 위도와 경도를 구해 저장하는 방법 - 2 (spring boot, react, Geocoding)

햐얀하늘·2023년 11월 17일
0

받은 주소를 활용해 spring boot 내부에서 위도 경도알아내 저장하기

개요

프론트에서 서울특별시 영등포구 여의대로 108(더현대)의 주소를 받아온 뒤 해당 주소의 위도와 경도를 가져오기 위해서 Geocoding을 이용해 위도 경도를 받아와 보자

1. Geocoding 등록

네이버 클라우드에 Geocoding 사용 등록을 해야한다.
등록한 뒤 Client-ID와 Client-Secret을 기억할 필요가 있다.

X-NCP-APIGW-API-KEY-ID : Client-ID
X-NCP-APIGW-API-KEY : Client-secret

형식으로 Header에 보내줘야 Geocoding이 사용된다.

2. Geocoding API 활용

URL :
https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query=서울특별시 영등포구 여의대로 108

Header에 아래 키를 넣어준다.
X-NCP-APIGW-API-KEY-ID : Client-ID
X-NCP-APIGW-API-KEY : Client-secret

query뒤에 주소를 넣으면 해당 주소의 위도 경도 포함 모든 정보가 나온다.

{
    "status": "OK",
    "meta": {
        "totalCount": 1,
        "page": 1,
        "count": 1
    },
    "addresses": [
        {
            "roadAddress": "서울특별시 영등포구 여의대로 108 파크원",
            "jibunAddress": "서울특별시 영등포구 여의도동 22 파크원",
            "englishAddress": "108, Yeoui-daero, Yeongdeungpo-gu, Seoul, Republic of Korea",
            "addressElements": [
                {
                    "types": [
                        "SIDO"
                    ],
                    "longName": "서울특별시",
                    "shortName": "서울특별시",
                    "code": ""
                },
                {
                    "types": [
                        "SIGUGUN"
                    ],
                    "longName": "영등포구",
                    "shortName": "영등포구",
                    "code": ""
                },
                {
                    "types": [
                        "DONGMYUN"
                    ],
                    "longName": "여의도동",
                    "shortName": "여의도동",
                    "code": ""
                },
                {
                    "types": [
                        "RI"
                    ],
                    "longName": "",
                    "shortName": "",
                    "code": ""
                },
                {
                    "types": [
                        "ROAD_NAME"
                    ],
                    "longName": "여의대로",
                    "shortName": "여의대로",
                    "code": ""
                },
                {
                    "types": [
                        "BUILDING_NUMBER"
                    ],
                    "longName": "108",
                    "shortName": "108",
                    "code": ""
                },
                {
                    "types": [
                        "BUILDING_NAME"
                    ],
                    "longName": "파크원",
                    "shortName": "파크원",
                    "code": ""
                },
                {
                    "types": [
                        "LAND_NUMBER"
                    ],
                    "longName": "22",
                    "shortName": "22",
                    "code": ""
                },
                {
                    "types": [
                        "POSTAL_CODE"
                    ],
                    "longName": "07335",
                    "shortName": "07335",
                    "code": ""
                }
            ],
            "x": "126.9271941",
            "y": "37.5266691",
            "distance": 0.0
        }
    ],
    "errorMessage": ""
}

이런 식으로 데이터가 나오는데 그렇다면 spring boot에서 Geocoding을 사용해보자

3. Spring boot Geocoding 활용해 데이터 저장하기(ex 축제 이벤트 저장)

  • Event 엔티티
package com.ssafy.bangrang.domain.event.entity;

import com.ssafy.bangrang.domain.event.api.request.EventUpdateDto;
import com.ssafy.bangrang.domain.event.api.request.UpdateEventRequestDto;
import com.ssafy.bangrang.domain.inquiry.entity.Inquiry;
import com.ssafy.bangrang.domain.stamp.entity.Stamp;
import com.ssafy.bangrang.domain.member.entity.WebMember;
import com.ssafy.bangrang.global.common.entity.CommonEntity;
import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "event")
@ToString(of = {"idx", "title"})
public class Event extends CommonEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "event_idx")
    private Long idx;

    @Column(name = "event_title", nullable = false, columnDefinition = "TEXT")
    private String title;

    @Column(name = "event_sub_title", columnDefinition = "TEXT")
    private String subTitle;

    @Column(name = "event_content", columnDefinition = "TEXT")
    private String content;

    @Column(name = "event_image", columnDefinition = "TEXT")
    private String image;
    // 이미지 1

    @Column(name = "event_subImage", columnDefinition = "TEXT")
    private String subImage;
    // 이미지 2

    @Column(name = "event_start_date")
    private LocalDateTime startDate;

    @Column(name = "event_end_date")
    private LocalDateTime endDate;

    @Column(name = "event_address", columnDefinition = "TEXT")
    private String address;

    @Column(name = "event_latitude")
    private Double latitude;

    @Column(name = "event_longitude")
    private Double longitude;

    @Column(name = "event_url", columnDefinition = "TEXT")
    private String eventUrl;
    // 행사 메인 페이지

    // 행사 등록한 사람
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_idx")
    private WebMember webMember;

    // 행사에 대한 문의사항
    @OneToMany(mappedBy = "event")
    private List<Inquiry> inquiries = new ArrayList<>();

    // 좋아요 수
    @OneToMany(mappedBy = "event")
    private List<Likes> likes = new ArrayList<>();

    @OneToOne(mappedBy = "event")
    private Stamp stamp;

    @Builder
    public Event(Long idx,String title, String subTitle, String content, String image, String subImage, LocalDateTime startDate, LocalDateTime endDate, String address, Double latitude, Double longitude, String eventUrl, WebMember webMember){
        this.idx = idx;
        this.title = title;
        this.subTitle = subTitle;
        this.content = content;
        this.image = image;
        this.subImage = subImage;
        this.startDate = startDate;
        this.endDate = endDate;
        this.address = address;
        this.latitude = latitude;
        this.longitude = longitude;
        this.eventUrl = eventUrl;

        this.changeWebMember(webMember);
    }

    public void changeWebMember(WebMember webMember) {
        this.webMember = webMember;
        webMember.getEvents().add(this);
    }

    /**
     * 이벤트 상태 변경
     */
    public void updateEvent(UpdateEventRequestDto updateEventRequestDto, double changelati, double changelong) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
        this.title=updateEventRequestDto.getTitle();
        this.subTitle=updateEventRequestDto.getSubTitle();
        this.content=updateEventRequestDto.getContent();
        this.startDate=LocalDateTime.parse(updateEventRequestDto.getStartDate(),formatter);
        this.endDate=LocalDateTime.parse(updateEventRequestDto.getEndDate());
        this.address=updateEventRequestDto.getAddress();
        this.latitude=changelati;
        this.longitude=changelong;
        this.eventUrl=updateEventRequestDto.getEventUrl();
    }
    public void updateEventImg(String ImgUrl) {
        this.image=ImgUrl;
    }
    public void updateEventSubImg(String ImgUrl) {
        this.subImage=ImgUrl;
    }
}

Geocoding 활용해 위도 경도 정보 가져오기

  • 전체 코드 :
    Json형태로 값이 오기 때문에 JsonNode 타입으로 데이터를 받아줘야한다.
public JsonNode getLatiLong (String address) throws Exception {
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-NCP-APIGW-API-KEY-ID", client_id);
        headers.add("X-NCP-APIGW-API-KEY", client_secret);
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

        String query = address;
        String url = "https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query="+query;

        log.info("네이버 Geocoding api요청 시작");
        ResponseEntity<String> response =
                restTemplate.exchange(url,
                        HttpMethod.GET,
                        new HttpEntity<>(null, headers),
                        String.class);
        String body = response.getBody();
        log.info("네이버 Geocoding api요청 완료");

        JsonNode root = objectMapper.readTree(body);
        JsonNode addresses = root.get("addresses");

        return addresses;
    }
  • Headers에 api key를 넣고 RestTemplate을 이용해 api 요청해보도록 하자
		HttpHeaders headers = new HttpHeaders();
		headers.add("X-NCP-APIGW-API-KEY-ID", client_id); // client_id 넣어주기
        headers.add("X-NCP-APIGW-API-KEY", client_secret); // client_secreit 넣어주기
        RestTemplate restTemplate = new RestTemplate(); // java에서 api 요청 보내기 위해 RestTemplate을 활용하기
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

        String query = address; // 어떤 주소 정보
        String url = "https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query="+query; // Geocoding 활용 API url
		// "네이버 Geocoding api요청 시작"
        ResponseEntity<String> response =
                restTemplate.exchange(url,
                        HttpMethod.GET,
                        new HttpEntity<>(null, headers),
                        String.class);
        String body = response.getBody(); // 가져온 데이터 body에 저장
        // "네이버 Geocoding api요청 완료"

        JsonNode root = objectMapper.readTree(body); // objectMapper를 활용해 읽어온 데이터 key value로 저장하기
        JsonNode addresses = root.get("addresses"); // 해당 주소의 위도와 경도 가져오기

        return addresses; // 위도 경도 리턴
  • Event를 만들어 저장해보자 전체 코드
@Override
@Transactional
public void createEvent(CreateEventRequestDto createEventRequestDto, MultipartFile image, MultipartFile subImage, UserDetails userDetails) throws Exception{
        WebMember user = webMemberRepository.findById(userDetails.getUsername())
                .orElseThrow(() -> new EmptyResultDataAccessException("해당 유저는 존재하지 않습니다.", 1));

        JsonNode addresses = getLatiLong(createEventRequestDto.getAddress());

        if (!addresses.isArray() || addresses.size() <= 0)
            throw new Exception("위도, 경도 오류");

        JsonNode firstAddress = addresses.get(0);
        double longitude = Double.parseDouble(firstAddress.get("x").asText());
        double latitude = Double.parseDouble(firstAddress.get("y").asText());

        String img;
        String subImg;

        if (image!=null && !image.isEmpty()) {
            String fileName =  s3Service.generateEventImageName(image, createEventRequestDto.getTitle());
            byte[] fileBytes = image.getBytes();
            img = s3Service.uploadToS3(fileName,fileBytes, image.getContentType());
        } else img = null;

        if (subImage!=null && !subImage.isEmpty()) {
            String fileName =  s3Service.generateEventSubImageName(subImage, createEventRequestDto.getTitle());
            byte[] fileBytes = subImage.getBytes();
            subImg = s3Service.uploadToS3(fileName,fileBytes, subImage.getContentType());
        } else subImg = null;

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

        Event event = Event.builder()
                .title(createEventRequestDto.getTitle())
                .subTitle(createEventRequestDto.getSubTitle())
                .address(createEventRequestDto.getAddress())
                .content(createEventRequestDto.getContent())
                .eventUrl(createEventRequestDto.getEventUrl())
                .longitude(longitude)
                .latitude(latitude)
                .startDate(LocalDateTime.parse(createEventRequestDto.getStartDate(),formatter))
                .endDate(LocalDateTime.parse(createEventRequestDto.getEndDate(),formatter))
                .image(img)
                .subImage(subImg)
                .webMember(user)
                .build();

        eventRepository.save(event);

        Event saveEvent = eventRepository.findByTitle(createEventRequestDto.getTitle())
                .orElseThrow();

        Stamp stamp = Stamp.builder()
                .name(saveEvent.getTitle())
                .event(saveEvent)
                .build();

        stampRepository.save(stamp);

    }
  • 이벤트 저장코드를 분석해보자
		WebMember user = webMemberRepository.findById(userDetails.getUsername())
                .orElseThrow(() -> new EmptyResultDataAccessException("해당 유저는 존재하지 않습니다.", 1)); // User 체크하기
		
		// geocoding을 활용해 찾아낸 위도와 경도 함수 활용
        JsonNode addresses = getLatiLong(createEventRequestDto.getAddress());
		
		// 만약 위도와 경도를 찾아내지 못할시 에러 발생
        if (!addresses.isArray() || addresses.size() <= 0)
            throw new Exception("위도, 경도 오류");
		
		// 위도와 경도를 각각 저장하고 Double 데이터 타입으로 변경
        JsonNode firstAddress = addresses.get(0);
        double longitude = Double.parseDouble(firstAddress.get("x").asText());
        double latitude = Double.parseDouble(firstAddress.get("y").asText());

		// img, subimg를 저장할 객체를 만들어줌
		String img;
        String subImg;
		
		// img를 s3에 저장하자
        if (image!=null && !image.isEmpty()) {
            String fileName =  s3Service.generateEventImageName(image, createEventRequestDto.getTitle()); // s3에 이미지의 이름을 저장하는 코드
            byte[] fileBytes = image.getBytes(); // 이미지를 bytes로 변환해 저장
            img = s3Service.uploadToS3(fileName,fileBytes, image.getContentType()); // s3에 이미지 주소, 파일, 콘텐츠타입을 가져와 저장하기
        } else img = null;

        if (subImage!=null && !subImage.isEmpty()) {
            String fileName =  s3Service.generateEventSubImageName(subImage, createEventRequestDto.getTitle());
            byte[] fileBytes = subImage.getBytes();
            subImg = s3Service.uploadToS3(fileName,fileBytes, subImage.getContentType());
        } else subImg = null;
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); 

        Event event = Event.builder() // 데이터를 저장 startDate와 endDate는 
                .title(createEventRequestDto.getTitle())
                .subTitle(createEventRequestDto.getSubTitle())
                .address(createEventRequestDto.getAddress())
                .content(createEventRequestDto.getContent())
                .eventUrl(createEventRequestDto.getEventUrl())
                .longitude(longitude)
                .latitude(latitude)
                .startDate(LocalDateTime.parse(createEventRequestDto.getStartDate(),formatter))
                .endDate(LocalDateTime.parse(createEventRequestDto.getEndDate(),formatter))
                .image(img)
                .subImage(subImg)
                .webMember(user)
                .build();

        eventRepository.save(event);
profile
나는 커서 개발자가 될거야!

0개의 댓글