윈도우) alt + shift + s + v 누르면 오버라이딩

 

 

빈을 등록하는 방식, 어노테이션, 의존 주입에 대해 배웠다.

 

MVC를 기준으로 어노테이션 설명

💡 C (클라이언트)→ F.C (프레젠테이션) → M (모델) → DB

 M 에는 서비스(비즈니스 계층) + DAO(퍼시스던트 계층) 포함

 

클라이언트 →  프레젠테이션 → 비즈니스계층 → 퍼시스던트계층

@Component    @Controller     @Service      @Repository

 

 

어노테이션의 bean 등록

 

@Component : 일반 클래스

@Controller : 사용자의 요청을 처리 ( xxxController )

@Service : 비즈니스 로직 ( xxxServicelmpl )

@Repository : 데이터 베이스 처리 ( xxxDAO )

 

Service 계층은 인터페이스가 클래스를 상속받는 구조 → 확장성 때문

 

스프링을 배울 때는 단순히 코딩을 따라 쳐서 결과가 나온 게 중요한 게 아니라 구조를 이해해야 한다. 어떤 코드가 어디서 쓰이는지 알아야 개발을 할 수 있음.

 

데이터 베이스에 연결해야 하기 때문에 DB 세팅을 해야 한다.

프로젝트의 build path 에 들어가서 ojdbc6를 연결한다. 맥에서는 11로 해야한다.

 

💡 윈도우에서 파일이 있는 경로 : C:\app\ITSC\product\11.2.0\dbhome_1\jdbc\lib

 

이렇게 추가해주고 apply and close를 하면 레퍼런스 러이브러리에 ojdbc6이 추가되어 있다.

sql 디벨로퍼로 넘어와서 데이터를 처리할 테이블을 만들어준다.

 

scott 계정 )

create table board(
seq number(5) primary key,
title varchar2(200),
writer varchar2(20),
content varchar2(2000),
regdate date DEFAULT sysdate,
cnt number(5) DEFAULT 0
);

데이터베이스에 연결되기 위해 DAO/VO를 생성해야한다.

다시 스프링에서 만들어주기.

 


그 전에 롬북 설치

 

터미널에서 )

C:\Users\ITSC>cd\

C:\>java -jar lombok.jar

STS 실행하는 경로에 롬북을 설치한다.

 

롬북을 배포하기 위해 maven에 들어가 <dependency> 부분을 긁어 pom.xml에 넣는다.

 

이 부분에 넣어줬다.

 


 

여기까지 하고 jdbc 환경설정을 위해 JDBCUtil.java 파일을 만들었다.

 

 

JDBCUtil.java

package com.springbook.biz.common;

import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Connection;

public class JDBCUtil {

	// JDBC 홤경설정
	// DB 연결
	public static Connection getConnection() {

		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
			return DriverManager.getConnection("jdbc:oracle:thin:@localhost:1522:orcl1", "scott", "tiger");
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;

	}

	// DB 연결 끊기
	public static void close(PreparedStatement stmt, Connection conn) {
		if (stmt != null) {
			try {
				if (!stmt.isClosed())
					stmt.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				stmt = null;
			}
		}

		if (conn != null) {
			try {
				if (!conn.isClosed())
					conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				conn = null;
			}
		}
	}

	public static void close(ResultSet rs, PreparedStatement stmt, Connection conn) {
		if (rs != null) {
			try {
				if (!rs.isClosed())
					rs.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				rs = null;
			}
		}

		if (stmt != null) {
			try {
				if (!stmt.isClosed())
					stmt.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				stmt = null;
			}
		}

		if (conn != null) {
			try {
				if (!conn.isClosed())
					conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				conn = null;
			}
		}
	}

}

 

 


 

인터페이스를 상속 받는 클래스 만드는 방법 )

인터페이스 add를 누르고 상속하려는 인터페이스를 선택한다.

 


 

 

BoardVO.java

package com.springbook.biz.board;

import java.util.Date;

public class BoardVO {
	private int seq;
	private String title;
	private String writer;
	private String content;
	private Date regDate;
	
	public int getSeq() {
		return seq;
	}
	public void setSeq(int seq) {
		this.seq = seq;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getWriter() {
		return writer;
	}
	public void setWriter(String writer) {
		this.writer = writer;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public Date getRegDate() {
		return regDate;
	}
	public void setRegDate(Date regDate) {
		this.regDate = regDate;
	}
	public int getCnt() {
		return cnt;
	}
	public void setCnt(int cnt) {
		this.cnt = cnt;
	}
	private int cnt;

	@Override
	public String toString() {
		return "BoardVO [seq=" + seq + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regDate="
				+ regDate + ", cnt=" + cnt + "]";
	}
}

 

BoardService.java (인터페이스)

package com.springbook.biz.board;

import java.util.List;

public interface BoardService {
	// crud 
	
	// 글 등록
	void insertBoard(BoardVO vo);
	
	// 글 수정 
	void updateBoard(BoardVO vo);

	// 글 삭제
	void deleteBoard(BoardVO vo);
	
	// 글 상세조회
	BoardVO getBoard(BoardVO vo);
	
	// 글 목록 조회
	List<BoardVO> getBoardList(BoardVO vo);
}

 

BoardDAO.java

package com.springbook.biz.board.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Repository;

import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.JDBCUtil;

@Repository("boardDAO")
public class BoardDAO {
	private Connection conn = null;
	private PreparedStatement stmt = null;
	private ResultSet rs = null;

	// SQL 명령어

	// 상수형태로 만들어 두었다.
	private final String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
			+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";

	private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
	private final String BOARD_DELETE = "delete board where seq=?";
	private final String BOARD_GET = "select * from board where seq=?";
	private final String BOARD_LIST = "select * from board order by seq desc";

	// 글 등록
	public void insertBoard(BoardVO vo) {
		System.out.println("===> JDBC로 insertBoard() 기능 처리");
		try {

			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_INSERT);
			stmt.setString(1, vo.getTitle());
			stmt.setString(2, vo.getWriter());
			stmt.setString(3, vo.getContent());
			stmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
	}

	// 글 수정

	// 글 삭제

	// 글 상세 조회

	// 글 목록 조회
	public List<BoardVO> getBoardList(BoardVO vo) {
		System.out.println("==> JDBC로 getBoardList() 기능 처리");

		List<BoardVO> boardlist = new ArrayList<>();

		try {

			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_LIST);
			rs = stmt.executeQuery();

			while (rs.next()) {
				BoardVO board = new BoardVO();
				board.setSeq(rs.getInt("SEQ"));
				board.setTitle(rs.getString("TITLE"));
				board.setWriter(rs.getString("WRITER"));
				board.setContent(rs.getString("CONTENT"));
				board.setRegDate(rs.getDate("REGDATE"));
				board.setCnt(rs.getInt("CNT"));

				boardlist.add(board);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}

		return boardlist;
	}

}

 

BoardServiceImpl.java

package com.springbook.biz.board.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;

@Service("boardService")
public class BoardServiceImpl implements BoardService {

	// boardDAO를 의존 주입해야 한다.
	@Autowired
	private BoardDAO boardDAO;

	@Override
	public void insertBoard(BoardVO vo) {
		// @Autowired를 했기 때문에 사용가능, @Autowired로 객체를 생성한거다.
		boardDAO.insertBoard(vo);

	}

	@Override
	public void updateBoard(BoardVO vo) {

	}

	@Override
	public void deleteBoard(BoardVO vo) {

	}

	@Override
	public BoardVO getBoard(BoardVO vo) {

		return null;
	}

	@Override
	public List<BoardVO> getBoardList(BoardVO vo) {

		return boardDAO.getBoardList(vo);
	}

}

 

applicationContext.xml → 패키지를 스캔할 수 있도록 패키지명 적어주기

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

  <!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
	<context:component-scan base-package="com.springbook.biz"></context:component-scan>
	

</beans>

 

BoardServiceClient.java (실행할 파일)

package com.springbook.biz.board;

import java.util.List;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class BoardServiceClient {

	public static void main(String[] args) {
		// BoardServiceImpl 가지고 와서 써야한다.
		AbstractApplicationContext container = new GenericXmlApplicationContext("applicationContext.xml");

		BoardService boardService = (BoardService) container.getBean("boardService");

		BoardVO vo = new BoardVO();

		// 글 등록
		vo.setTitle("임시제목");
		vo.setWriter("홍길동");
		vo.setContent("임시내용...");
		boardService.insertBoard(vo);

		// 글 목록 조회

		List<BoardVO> boardList = boardService.getBoardList(vo);
		for (BoardVO board : boardList) {
			System.out.println("-->" + board.toString());
		}
	}

}

여기까지 했을 때 BoardServiceClient.java를 실행하면 DB로 데이터가 넘어가야 한다.

 

 

파일의 구조를 보면 이렇다.

 

한 곳에 몰아넣는 구조도 아니고, 기능이 다른 여러 패키지를 만드는 등 정말 복잡하기 때문에 따라할 때도 집중 해야 한다.

 

기본적인 구조가 잡히면 그 안에 코드를 작성한다.

BoardDAO.java, BoardServiceImpl.java, BoardServiceClient.java 파일에 추가 코드 작업을 했다.

 

 

BoardDAO.java

package com.springbook.biz.board.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Repository;

import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.JDBCUtil;

import oracle.net.aso.i;
import oracle.net.aso.p;

@Repository("boardDAO")
public class BoardDAO {
	private Connection conn = null;
	private PreparedStatement stmt = null;
	private ResultSet rs = null;

	// SQL 명령어

	// 상수형태로 만들어 두었다.
	private final String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
			+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";

	private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
	private final String BOARD_DELETE = "delete board where seq=?";
	private final String BOARD_GET = "select * from board where seq=?";
	private final String BOARD_LIST = "select * from board order by seq desc";

	// 글 등록
	public void insertBoard(BoardVO vo) {
		System.out.println("===> JDBC로 insertBoard() 기능 처리");
		try {

			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_INSERT);
			stmt.setString(1, vo.getTitle());
			stmt.setString(2, vo.getWriter());
			stmt.setString(3, vo.getContent());
			stmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
	}

	// 글 수정
	public void updateBoard(BoardVO vo) {
		System.out.println("===> JDBC로 updateBoard() 기능 처리");
		
		try {
			
			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_UPDATE);
			stmt.setString(1, vo.getTitle());
			stmt.setString(2, vo.getContent());
			stmt.setInt(3, vo.getSeq());
			stmt.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
	}

	// 글 삭제
	public void deleteBoard(BoardVO vo) {
		System.out.println("===> JDBC로 deleteBoard() 기능 처리");
		
		try {
			
			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_DELETE);
			stmt.setInt(1, vo.getSeq());
			stmt.executeUpdate();
			
		} catch (Exception e){
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}
		
	}

	// 글 상세 조회
	public BoardVO getBoard(BoardVO vo) {
		
		BoardVO board = new BoardVO();
		System.out.println("===> JDBC로 getBoard() 상세 보기  처리");
		
		try {

			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_GET);
			stmt.setInt(1, vo.getSeq());
			rs = stmt.executeQuery();
			
			if (rs.next()) {
				
				board.setSeq(rs.getInt("SEQ"));
				board.setTitle(rs.getString("TITLE"));
				board.setWriter(rs.getString("WRITER"));
				board.setContent(rs.getString("CONTENT"));
				board.setRegDate(rs.getDate("REGDATE"));
				board.setCnt(rs.getInt("CNT"));
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(rs, stmt, conn);
		}
		
		return board;
	}

	// 글 목록 조회
	public List<BoardVO> getBoardList(BoardVO vo) {
		System.out.println("==> JDBC로 getBoardList() 기능 처리");

		List<BoardVO> boardlist = new ArrayList<>();

		try {

			conn = JDBCUtil.getConnection();
			stmt = conn.prepareStatement(BOARD_LIST);
			rs = stmt.executeQuery();

			while (rs.next()) {
				BoardVO board = new BoardVO();
				board.setSeq(rs.getInt("SEQ"));
				board.setTitle(rs.getString("TITLE"));
				board.setWriter(rs.getString("WRITER"));
				board.setContent(rs.getString("CONTENT"));
				board.setRegDate(rs.getDate("REGDATE"));
				board.setCnt(rs.getInt("CNT"));

				boardlist.add(board);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(stmt, conn);
		}

		return boardlist;
	}

}

 

BoardServiceImpl.java

package com.springbook.biz.board.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;

@Service("boardService")
public class BoardServiceImpl implements BoardService {

	// boardDAO를 의존 주입해야 한다.
	@Autowired
	private BoardDAO boardDAO;

	@Override
	public void insertBoard(BoardVO vo) {
		// @Autowired를 했기 때문에 사용가능, @Autowired로 객체를 생성한거다.
		boardDAO.insertBoard(vo);

	}

	@Override
	public void updateBoard(BoardVO vo) {
		
		boardDAO.updateBoard(vo);
	}

	@Override
	public void deleteBoard(BoardVO vo) {

		boardDAO.deleteBoard(vo);
	}

	@Override
	public BoardVO getBoard(BoardVO vo) {

		return boardDAO.getBoard(vo);
	}

	@Override
	public List<BoardVO> getBoardList(BoardVO vo) {

		return boardDAO.getBoardList(vo);
	}

}

 

BoardServiceClient.java

package com.springbook.biz.board;

import java.util.List;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class BoardServiceClient {

	public static void main(String[] args) {
		// BoardServiceImpl 가지고 와서 써야한다.
		AbstractApplicationContext container = new GenericXmlApplicationContext("applicationContext.xml");

		BoardService boardService = (BoardService) container.getBean("boardService");

		BoardVO vo = new BoardVO();

		// 글 등록
//		vo.setTitle("임시제목");
//		vo.setWriter("홍길동");
//		vo.setContent("임시내용...");
//		boardService.insertBoard(vo);
		
		// 글 상세 조회
//		vo.setSeq(4);
//		BoardVO boardVO = boardService.getBoard(vo);
//		System.out.println(boardVO.toString());
		
		// 글 삭제
//		vo.setSeq(1);
//		boardService.deleteBoard(vo);
		
		// 글 수정
		// 제목, 내용
		vo.setSeq(4);
		vo.setTitle("진정한 제목");
		vo.setContent("진정한 내용...");
		boardService.updateBoard(vo);

		// 글 목록 조회
		List<BoardVO> boardList = boardService.getBoardList(vo);
		for (BoardVO board : boardList) {
			System.out.println("-->" + board.toString());
		}
	}

}

 

실행화면 )

글 수정, 삭제, 조회까지 잘 되는 모습이다.

오늘부터는 스프링 수업을 한다.

 

STS 다운받기

수업에 필요한 버전으로 다운 받기

3.9.12 버전의 맨 위에 있는 zip 파일을 받는다.

 

위 버전의 스프링은 자바 11버전을 써야 버전 충돌을 방지할 수 있어 현재 설치된 자바 17버전을 지우고 자바 11을 다운 받는다.

 

 

STS를 실행하기 전 springworkspace를 만들어준다.

이제 C:\work\utils\sts-bundle\sts-3.9.12.RELEASE 경로로 STS가 설치된 것을 확인한 후 springworkspace 로 경로설정을 하고 들어가면 이런 창이 뜬다. 새로운 파일을 만들기 위해 보면 spring legacy project가 보인다.

 

위 파일을 springworkspace의 메타데이터, 위 경로에 넣고 다시 STS를 열어 spring legacy project 로 새로운 파일을 만들 때 보면

 

전엔 보이지 않았던 simple spring utility project, spring MVC project 가 생겼다.

 

 

그런데 spring legacy project가 뭘까?

Spring Legacy Project는 스프링 프레임워크의 초기 버전에서 제공하던 다양한 기능을 포함한 프로젝트 유형이다.

주로 기존의 Spring MVC, Spring Web Flow, 그리고 여러 레거시 기술들을 활용하여 개발된 애플리케이션을 지원한다. 이를 통해 개발자는 레거시 시스템과의 호환성을 유지하면서도 스프링의 장점을 활용할 수 있다.

 

더 이해하기 쉽게 참고하기 → https://cuna.tistory.com/133

 

[Spring] Spring Legacy Project? Maven?

당신은 그냥 책/유튜브/강의에서 하라는 대로 Spring Legacy Project를 선택해서 프로젝트를 만들고 어쩌다보니 Maven 업데이트를 하라고 해서 그렇게 하고 있지는 않은지?? 나는 그랬다.. 그래서 이번

cuna.tistory.com

 

스프링을 쓰기 위해 톰캣 서버를 연결한다.

 

 

 

스프링

: 구조되어진 틀 안에 우리가 내용만 넣어 처리하는 방식이다.

 

spring MVC project → maven 과 여러 라이브러리가 세팅되어 생성되는 어플리케이션이다.

 

스프링은 POJO방식을 기반으로 한 웹 프레임워크이고,

IoC/ DI, AOP, PSA spring의 주요 기술을 활용해 POJO 기반의 구성을 이루게 되었다.

 

스프링은 객체 생성에서 소멸까지 컨테이너에서 관리해준다.

POJO 방식을 좀 더 효율적으로 사용할 수 있다.

 

ex) ‘물감, 붓 등 다 세팅해둘테니 개발자 너가 그리고 싶은 것을 그려라’ 하는 것이 스프링

 

Plain Old Java Object (POJO)

더보기

Plain Old Java Object (POJO)는 특정한 규약이나 프레임워크에 종속되지 않은 간단한 자바 객체입니다. POJO는 다음 특징을 가집니다:

  • 특정한 인터페이스나 클래스를 구현할 필요 없음: POJO는 특정한 인터페이스나 클래스를 구현할 필요 없이 자유롭게 정의할 수 있습니다.
  • 캡슐화: POJO는 필드와 메서드를 통해 데이터를 캡슐화합니다.
  • 기본적인 자바 클래스: POJO는 자바의 기본적인 클래스로, 특별한 애노테이션이나 설정이 필요 없습니다.

이러한 특성 덕분에 POJO는 쉽게 작성, 유지보수, 테스트가 가능하며, 다양한 자바 프레임워크와 호환됩니다.

 

 

 

스프링에서 프로젝트 만들기

프로젝트 세팅은 매번 해야한다.

 

Spring Legacy Project → spring MVC project →

패키지 설정에 ex) com.springbook.biz 이런 식으로 . . 구성으로 만들어서 설정해주기

프로젝트가 만들어지는데 시간이 걸리니 기다려야 한다. 그 시간 동안 이것저것 누르면 오류의 원인이 된다.

자바의 버전을 바꿔준다.

 

 

workspace와 html, css, jsp를 UTF-8 로 맞춰준다.

 

홈컨트롤러 파일로 들어가 실행 후 위와 같은 주소로 들어가면 Hello world! 화면을 볼 수 있다.

 

 

pom.xml 에서는 내가 설치한 버전에 맞게 버전 수정을 해주고 톰캣을 다시 시작해 문제 없이 돌아가는지 확인해주면 프로젝트 세팅은 끝난다.

 

 

파일을 만들고 작업할 위치

  • src/main/java → java 파일
  • src/main/resources → xml 파일
  • WEB-INF → jsp 파일

 

스프링에서는 WEF-INF 아래 jsp를 넣어서 사용한다. → jsp는 그렇게 사용할 수 없어서 둘의 이런 차이가 보안을 강화해준다.

 

 

 

스프링의 3대 요소

IOC (Inversion of Controller) : 제어의 역전

 

객체의 생성부터 생명주기 관리까지 모든 객체에 대한 제어권을 스프링이 가져간다.

만드는 것은 개발자가, 관리는 스프링 컨테이너가

개발자의 간섭은 최소화한다.

구조가 비슷하면 인터페이스를 통해 표준화 시킨다.

 

 

객체 지향적 프로그램의 기본 구성 :

사용하려는 클래스가 어떤 클래스를 직접 사용하는게 아니라 중개되는 역할의 클래스(인터페이스)를 통해 접근하는 것이 결합도가 낮은 프로그램이다. → 다형성을 구현할 수 있는 형태

결합도가 낮은 프로그램은 표준화를 사용할 수 있다.

 

 

스프링워크는 패턴의 집합체라고 할 수 있다.

 

스프링 워크의 디자인 패턴 )

  • 팩토리 패턴

생성을 위한 클래스를 만들어 다른 클래스에 접근한다.

 

스프링에서

bean = 객체

 

BeanFactory.java

package polymorphism;

public class BeanFactory {
	// bean == 객체
	
	// getBean 이라는 매개변수를 받아서 객체를 생성하겠다
	// 모든 타입에 사용할 수 있는 Object
	public Object getBean(String beanName) {
	
		if (beanName.equals("samsung")) {
			return new SamsungTV();
		}else {
			return new LgTV();
		}
	}
}

위와 같이 factory 파일이 만들어져야 한다.

 

  • 싱글톤 패턴

동일타입으로 객체를 한번 이상 생성이 불가

		// new GenericXmlApplicationContext("applicationContext.xml");
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		TV tv = (TV)factory.getBean("tv"); // 싱글톤 패턴
		TV tv2 = (TV)factory.getBean("tv");
		
		System.out.println(tv); // 싱글톤 주소값 : polymorphism.SamsungTV@25084a1e
		System.out.println(tv2); // 싱글톤 주소값 : polymorphism.SamsungTV@25084a1e 
		// 싱글톤 패턴을 사용하지 않으면 주소가 달라진다, 동일타입으로 객체생성 가능
		
		tv.powerOn();
		tv.powerOff();
		tv.volumeUp();
		tv.volumeDown();

 

XML파일에 정보 등록

= SamsungTV tv = new SamsungTV();
<bean id="" class=""></bean>


ex)

<bean id="tv" class="polymorphism.SamsungTV"></bean>

= SamsungTV tv = new SamsungTV();

위와 아래가 같은 의미다!

id 에는 생성할 객체, class에는 패키지 경로

 


인터페이스를 표준화 시킬 수 있는 경우)

 

SamsungTV.java

package polymorphism;

public class SamsungTV implements TV{
	
	public void powerOn() {
		System.out.println("SamsungTV -- 전원 켠다");
	}
	
	public void powerOff() {
		System.out.println("SamsungTV -- 전원 끈다");
	}
	
	public void volumeUp() {
		System.out.println("SamsungTV -- 소리 올리다");
	}
	
	public void volumeDown() {
		System.out.println("SamsungTV -- 소리 내리다");
	}

}

 

Lg.java

package polymorphism;

public class LgTV implements TV{
	
	@Override
	public void powerOn() {
		System.out.println("LgTV -- 전원 켠다");
		
	}

	@Override
	public void powerOff() {
		System.out.println("LgTV -- 전원 끈다");
		
	}

	@Override
	public void volumeUp() {
		System.out.println("LgTV -- 소리 올리다");
		
	}

	@Override
	public void volumeDown() {
		System.out.println("LgTV -- 소리 내리다");
		
	}

}

 

위 두 파일의 코드는 구조가 비슷하기 때문에 인터페이스를 통해 표준화 시킬 수 있다.


 

 

DI (Dependency Injection) : 의존성 주입

  • 생성자 의존 주입
  • Setter 메소드 의존 주입
  • 멤버변수 의존 주입
  • 어노테이션 방식의 bean 등록
  • 자동 의존 주입 방식

 

기존 코드

A a = new A();

B b = new B(a);

스프링에서는 둘의 관계만 표시해두면 된다.

 

 

 

생성자 의존 주입

 

TVUser.java

package polymorphism;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class TVUser {

	public static void main(String[] args) {
		
		// new 라는 키워드를 사용하지 않는다 
		// org.springframework 아래 있는 패키지 사용을 사용하는 경우가 대부분이다
		// 잘 모르겠으면 org.springframework 로 임포트
		// 아래는 BeanFactory factory = new BeanFactory(); 와 같은 의미의 코드이다.
		
	  new GenericXmlApplicationContext("applicationContext.xml");
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		TV tv = (TV)factory.getBean("tv"); // 싱글톤 패턴
		TV tv2 = (TV)factory.getBean("tv");
		
		System.out.println(tv); // 싱글톤 주소값 : polymorphism.SamsungTV@25084a1e
		System.out.println(tv2); // 싱글톤 주소값 : polymorphism.SamsungTV@25084a1e 
		// 싱글톤 패턴을 사용하지 않으면 주소가 달라진다.
		
		tv.powerOn();
		tv.powerOff();
		tv.volumeUp();
		tv.volumeDown();
	}

}

 

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- = SamsungTV tv = new SamsungTV(); -->
	<!-- <bean id="" class=""></bean> -->
	
  <!-- <bean id="tv" class="polymorphism.SamsungTV"></bean> 싱글톤 패턴 사용  -->
	<!-- scope="prototype"을 추가하면 싱글톤 패턴 사용을 하지 않을 수 있다. -->
	
	<!-- 생성자 의존 주입 -->
	<bean id="tv" class="polymorphism.SamsungTV" scope="prototype">
		<!-- 괸계설정 -->
		<constructor-arg ref="apple"></constructor-arg>
		<!-- 기본형 관계설정 시 value를 쓴다 -->
		<constructor-arg value="10000"></constructor-arg>
 </bean> 

</beans>

 

SamsungTV.java

package polymorphism;

public class SamsungTV implements TV{
	
	// 소니스피커를 사용해야 한다
	private SonySpeaker speaker;
	private int price;
	
	public SamsungTV() {
		System.out.println("===> SamsungTV 객체생성");
	}
	
	// 생성자 의존주입
	public SamsungTV(SonySpeaker speaker) {
		System.out.println("===> SamsungTV(2) 객체생성");
		this.speaker = speaker;
	}
	
	public SamsungTV(SonySpeaker speaker, int price) {
		System.out.println("===> SamsungTV(3) 객체생성");
		this.speaker = speaker;
		this.price = price;
	}
	
	public void powerOn() {
		System.out.println("SamsungTV -- 전원 켠다. (가격: " + price + ")");
	}

	public void powerOff() {
		System.out.println("SamsungTV -- 전원 끈다.");
	}
	
	public void volumeUp() {
		speaker.volumeUp();
		// speaker = new SonySpeaker();
		// speaker.volumeUp();
		// System.out.println("SamsungTV -- 소리 올리다");
	}
	
	public void volumeDown() {
		speaker.volumeDown();
		// speaker = new SonySpeaker();
		// speaker.volumeDown();
		// System.out.println("SamsungTV -- 소리 내리다");
	}

}

 

 

 

Setter 메소드 의존 주입

 

TVUser.java

package polymorphism;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class TVUser {

	public static void main(String[] args) 
		
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		TV tv = (TV)factory.getBean("tv"); // 싱글톤 패턴
		
		tv.powerOn();
		tv.powerOff();
		tv.volumeUp();
		tv.volumeDown();
	}

}

 

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Setter 메소드 의존 주입 -->
	<bean id="tv" class="polymorphism.SamsungTV" scope="prototype">
	<property name="speaker" ref="sony"></property>
	<property name="price" value="10000"></property>
	</bean>
	
	<bean id="sony" class="polymorphism.SonySpeaker"></bean>
	<bean id="apple" class="polymorphism.AppleSpeaker"></bean>

</beans>

 

 

 

어노테이션 방식의 bean 등록

@Component

 

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
	<context:component-scan base-package="polymorphism"></context:component-scan>

 

LgTV.java

package polymorphism;

import org.springframework.stereotype.Component;

// id="tv" 쓴 것과 같은 의미
@Component("tv")
public class LgTV implements TV{
	
	@Override
	public void powerOn() {
		System.out.println("LgTV -- 전원 켠다");
		
	}

	@Override
	public void powerOff() {
		System.out.println("LgTV -- 전원 끈다");
		
	}

	@Override
	public void volumeUp() {
		System.out.println("LgTV -- 소리 올리다");
		
	}

	@Override
	public void volumeDown() {
		System.out.println("LgTV -- 소리 내리다");
		
	}

}

 

TVUser.java

package polymorphism;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class TVUser {

	public static void main(String[] args) {
		

		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		TV tv = (TV)factory.getBean("tv"); 
		
		tv.powerOn();
		tv.powerOff();
		tv.volumeUp();
		tv.volumeDown();
	}

}

 

여기서 호출하는 tv는 LgTV 이다.

LgTV 에 어노테이션을 붙여줬기 때문.

 

 

 

자동 의존 주입

@Autowired

 

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
	<context:component-scan base-package="polymorphism"></context:component-scan>
	

</beans>

 

LgTV.java

package polymorphism;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

// id="tv" 쓴 것과 같은 의미
@Component("tv")
public class LgTV implements TV{
	
	// 자동 의존주입 방식 : 선언된 변수의 타입으로 의존 주입한다.
	// 어노테이션 @Autowired
	// @Qualifier 아이디 값으로 구분
	@Autowired
	@Qualifier("apple")
	private Speaker speaker;
	
	public LgTV() {
		System.out.println("===> LgTV 객체생성");
	}

	@Override
	public void powerOn() {
		System.out.println("LgTV -- 전원 켠다");
		
	}

	@Override
	public void powerOff() {
		System.out.println("LgTV -- 전원 끈다");
		
	}

	@Override
	public void volumeUp() {
		speaker.volumeUp();
		// System.out.println("LgTV -- 소리 올리다");
		
	}

	@Override
	public void volumeDown() {
		speaker.volumeDown();
		// System.out.println("LgTV -- 소리 내리다");
		
	}

}

 

AppleSpeaker.java

package polymorphism;

import org.springframework.stereotype.Component;

@Component("apple")
public class AppleSpeaker implements Speaker {
	
	public AppleSpeaker() {
		System.out.println("===> AppleSpeaker 객체생성");
	}

	public void volumeUp() {
		System.out.println("AppleSpeaker 소리 울린다");
	}
	
	public void volumeDown() {
		System.out.println("AppleSpeaker 소리 내린다");
	}
}

 

SonySpeaker.java

package polymorphism;

import org.springframework.stereotype.Component;

@Component("sony")
public class SonySpeaker implements Speaker{
	
	public SonySpeaker() {
		System.out.println("===> SonySpeaker 객체생성");
	}
	
	public void volumeUp() {
		System.out.println("SonySpeaker 소리 울린다");
	}
	
	public void volumeDown() {
		System.out.println("SonySpeaker 소리 내린다");
	}
	
}

 

TVUser.java

package polymorphism;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class TVUser {

	public static void main(String[] args) {
		
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		TV tv = (TV)factory.getBean("tv"); 
		
		tv.powerOn();
		tv.powerOff();
		tv.volumeUp();
		tv.volumeDown();
	}

}

 

페이스북 로그인 기능 구현 마무리

 

어제 잘 안됐던 시퀸스가 없다고 DB에 값이 들어가지 않았던 회원가입 문제를 해결했다!

값이 잘 들어간다.

원인 : 계정이 달라졌는데 driver 설정을 바꾸지 않아서 다른 계정에서 자꾸 시퀸스를 찾으니 오류가 나왔던 거다.

 

해결 : memberProcess에 새로운 계정을 연결해 드라이버 정보를 바꿔준다.

   // 드라이버 정보를 바꿔줘야 한다!!
   String dirver = (String)application.getInitParameter("OracleDriver");
   String url = (String)application.getInitParameter("OracleURL");
   
   OauthMemberDAO dao = new OauthMemberDAO(dirver, url, "oauth", "1234");

 


 

바뀐 코드 전문

 

memberProcess.jsp

<%@page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="oauth.member.OauthMemberDTO"%>
<%@page import="oauth.member.OauthMemberDAO"%>
<%@page import="utils.JSFunction"%>


<%
   OauthMemberDTO dto = new OauthMemberDTO();
   
	String mid = request.getParameter("mid");
	String pass = request.getParameter("pass");
	String email = request.getParameter("email");
	String domain = request.getParameter("domain");
	String name = request.getParameter("name");
   
   System.out.print(mid);
   System.out.print(pass);
   System.out.print(email);
   System.out.print(domain);
   System.out.print(name);
   
   dto.setMid(mid);
   dto.setPass(pass);
   dto.setEmail(email);
   dto.setName(name);
   dto.setDomain(domain);
   
   // 드라이버 정보를 바꿔줘야 한다!!
   String dirver = (String)application.getInitParameter("OracleDriver");
   String url = (String)application.getInitParameter("OracleURL");
   
   OauthMemberDAO dao = new OauthMemberDAO(dirver, url, "oauth", "1234");
   
   int result = dao.insertmember(dto);
   
   if(result > 0){
      response.sendRedirect("facebookOauth2.html");
   }else{
      JSFunction.alertBack("회원가입에 실패하였습니다.", out);
   }
   
%>

 

OauthMemberDAO.java

package oauth.member;
import common.JDBConnect;

public class OauthMemberDAO extends JDBConnect{   
		
   public OauthMemberDAO(String driver, String url, String id, String pwd) {
		super(driver, url, id, pwd);
	}

   public int insertmember(OauthMemberDTO dto) {
      
      int result = 0;
         
      try {
    	  
         String query = " insert into Member "
                  + " values (seq_member_mno.nextval,?,?,?,?,?,sysdate) ";
            
         psmt = con.prepareStatement(query);
         psmt.setString(1, dto.getMid());
         psmt.setString(2, dto.getName());
         psmt.setString(3, dto.getPass());
         psmt.setString(4, dto.getEmail());
         psmt.setString(5, dto.getDomain());
            
         result = psmt.executeUpdate();
            
      }catch (Exception e) {
         e.printStackTrace();
      }
         
      return result;   
   }
}

 

OauthMemberDTO.java (수정 없음)

package oauth.member;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class OauthMemberDTO {
	private String mno;
	private String mid;
	private String name;
	private String pass;
	private String email;
	private String domain;
	private String regidate;
}

 

member.jsp (수정 없음)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
   String name = request.getParameter("name");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
   #wrap {
      width:800px;
      margin : 20px auto;
   }
   
   input[type=text],input[type=password] {
      width:95%;
   }
</style>
</head>
<body>
   <div id="wrap">
      <h1>회원가입페이지</h1>
      <form method="post" action="memberProcess.jsp">
         <table border="1" width="100%">
            <colgroup>
               <col width="30%" />
               <col width="*" />
            </colgroup>
            <tr>
               <td>아이디</td>
               <td>
                  <input type="text" name="mid" />
               </td>
            <tr>
            <tr>
               <td>비밀번호</td>
               <td>
                  <input type="password" name="pass" />
               </td>
            <tr>
            <tr>
               <td>이름</td>
               <td>
                  <input type="text" name="name" value="${param.name }" />
               </td>
            <tr>
            
            <tr>
               <td>이메일</td>
               <td>
                  <input type="text" name="email" value="${param.email }" />
               </td>
            <tr>
            <tr>
               <td>도메인</td>
               <td>
                  <input type="text" name="domain" value="${param.domain }"/>
               </td>
            <tr>
         </table>
         <input type="submit" value="회원가입" />
         <input type="reset" value="취소">
      </form>
   </div>
</body>
</html>

 

facebookOauth2.html (수정 없음)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script>

    function statusChangeCallback(response) {  // Called with the results from FB.getLoginStatus().
	    console.log('statusChangeCallback');
	    console.log(response);                   // The current login status of the person.
	    
	    if (response.status === 'connected') {   // Logged into your webpage and Facebook.
	      let domain = response.authResponse.graphDomain; // 도메인 정보 화면에 표시
	      document.querySelector('#authBtn').value = 'Logout';
	    
	    // 로그인이 성공했을 때 토큰
	    // '/me'를 쓰는 이유는 접속한 사람(나)의 정보를 가지고 오기 때문
	    // fields에서 받아올 수 있는 값을 확인하고 받아오고 싶은 값을 가지고 온다.
	      FB.api('/me',{fields:'email,name'}, function(response) {
	    	  // 로그인 성공했을 때 화면에 보여줄 메세지
	    	  // response 응답
	          document.querySelector('#name').innerHTML = response.name + "님 로그인 성공(" + response.email + ")" + domain;
	    	  window.location.href = 'member.jsp?name=' + response.name + "&email=" + response.email + "&domain=" + domain;
	        });
	    
	    } else {                                 // Not logged into your webpage or we are unable to tell.
	      
	      document.querySelector('#authBtn').value = 'Login';
	    // 로그아웃 후에는 이름, 이메일 정보가 안보이게 처리
	      document.querySelector('#name').innerHTML = "";
	        
	    }
    }
  
	window.fbAsyncInit = function() {
	    FB.init({
	      appId      : '____',        // 페이스북에서 발급받은 내 앱ID을 넣어야 한다.
	      cookie     : true,                     // Enable cookies to allow the server to access the session.
	      xfbml      : true,                     // Parse social plugins on this webpage.
	      version    : 'v18.0'                   // Use this Graph API version for this call.
	    });
	    
	    FB.getLoginStatus(function(response) {   // Called after the JS SDK has been initialized.
	        statusChangeCallback(response);        // Returns the login status.
      });
    };
</script>

   <!-- 로그인과 로그아웃을 하는 버튼 -->
   <input type="button" id="authBtn" value="페이스북 로그인" onclick="
         if(this.value === 'Login'){
            //로그아웃 상태
            FB.login(function(response){
                 // handle the response 
                 console.log('logout => response');
                 statusChangeCallback(response);
            });
         }else{
            //로그인 상태
            FB.logout(function(response) {
                  // Person is now logged out
                  console.log('login => response');
                  statusChangeCallback(response);
            });
         }
      ">
      
	<span id="name"></span>

<!-- sdk 로드  -->
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js"></script>
</body>
</html>

 


 

 

 

간편로그인 후 페이지 경로 설정

 

간편로그인 후에는 이미 회원인지, 아닌지 확인이 필요

이미 회원이라면 로그인 완료가 된 것. 회원이 아니라면 회원 가입 절차를 밟기 위해 인증 화면으로 이동해야 한다.

이런 것들은 프로그램을 짜기 전 항상 고민해야 한다.

facebookOauth2 에서 name, email, domain 정보를 가지고 와 기존 회원 유무를 판단,

기존 회원 유무를 판단한 후에는 기존 회원이라면 로그인 화면,

기존 회원이 아니라면 회원 가입 화면으로 이동

기존회원 유무를 판단하기 위해 controller를 만들어야한다.

 

 

 

내가 만든 코드)

LoginPageController.java

package oauth.member;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oauth.member.OauthMemberDAO;
import utils.JSFunction;

@WebServlet("/OauthLogin.do")
public class LoginPageController extends HttpServlet {
    
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // doGet 사용 안함, POST로만 받도록 설정
        resp.sendRedirect("index.jsp");
   }

   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      // 로그인과 회원가입 요청을 구분하여 처리해야한다.
      String action = req.getParameter("action");
      String email = req.getParameter("email");
      String domain = req.getParameter("domain");
      String name = req.getParameter("name");
      
      OauthMemberDAO dao = new OauthMemberDAO("driver", "url", "oauth", "1234");
   
      
      if ("로그인".equals(action)) {
         boolean confirmed = dao.confirmEmail(email, domain);
         if (confirmed) {
            JSFunction.alertLocation(resp, "기본회원 로그인 연동 페이지로 이동합니다.", "../Oauth/LoginMember.jsp");
         }else {
            JSFunction.alertBack(resp, "회원정보가 없습니다.");
         }
         
      } else if ("회원가입".equals(action)) {
         req.setAttribute("name", name);
            req.setAttribute("email", email);
            req.setAttribute("domain", domain);
            req.getRequestDispatcher("../Oauth/member.jsp").forward(req, resp);
         
      }
   }
      
}

강사님은 doGet()으로 받았는데 나는 doPost()로 받아서 방식이 조금 다르다. 어쩌면 그것 때문일지도,, ;; 코드가 잘 돌아가지 않는다. 너무 종합적인 문제가 있어서 차분히 다시 해봐야 할 듯.

 

OauthMemberDAO.java

package oauth.member;
import common.JDBConnect;

public class OauthMemberDAO extends JDBConnect{   
      
   public OauthMemberDAO(String driver, String url, String id, String pwd) {
      super(driver, url, id, pwd);
   }

   public int insertmember(OauthMemberDTO dto) {
      
      int result = 0;
         
      try {
         
         String query = " insert into Member "
                  + " values (seq_member_mno.nextval,?,?,?,?,?,sysdate) ";
            
         psmt = con.prepareStatement(query);
         psmt.setString(1, dto.getMid());
         psmt.setString(2, dto.getName());
         psmt.setString(3, dto.getPass());
         psmt.setString(4, dto.getEmail());
         psmt.setString(5, dto.getDomain());
            
         result = psmt.executeUpdate();
            
      }catch (Exception e) {
         e.printStackTrace();
      }
         
      return result;   
   }
   
   public boolean confirmEmail(String email, String domain) {
      
      boolean flag = false;
      
      try {
         
         String query = " select count(*) from member "
               + " where email = ? and domain = ? ";
         
         psmt = con.prepareStatement(query);
         psmt.setString(1, email);
         psmt.setString(2, domain);
         rs = psmt.executeQuery();
         
         if (rs.next() && rs.getInt(1) > 0) {
            flag = true;
         }
      }catch (Exception e) {
      System.out.println("회원 정보 확인 중 오류 발생");
      e.printStackTrace();
   }
      
      return flag;
   }
}

나는 불리언 타입으로, 강사님은 int로 코드를 작성

 


강사님 코드 )

 

LoginPageController.java

package oauth.member;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oauth.member.OauthMemberDAO;
import utils.JSFunction;

@WebServlet("/OauthMemberController")
public class LoginPageController extends HttpServlet {
    
	 @Override
	 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		String name = req.getParameter("name");
		String email = req.getParameter("email");
		String domain = req.getParameter("domain");
		
		
		OauthMemberDTO dto = new OauthMemberDTO();
		dto.setName(name);
		dto.setEmail(email);
		dto.setDomain(domain);
		
		ServletContext application = req.getServletContext();
		
		String driver = (String)application.getInitParameter("OracleDriver");
		String url = (String)application.getInitParameter("OracleURL");
		
		OauthMemberDAO dao = new OauthMemberDAO(driver, url, "oauth", "1234");
		int result = dao.selectCount(dto);
		
		if (result> 0) {
			resp.sendRedirect("Oauth/LoginForm.jsp");
		}else {
			req.getRequestDispatcher("/Oauth/Member.jsp").forward(req, resp);
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	
	}
		
}

 

OauthMemberDAO.java

package oauth.member;
import common.JDBConnect;

public class OauthMemberDAO extends JDBConnect{   
		
   public OauthMemberDAO(String driver, String url, String id, String pwd) {
		super(driver, url, id, pwd);
	}

   public int insertmember(OauthMemberDTO dto) {
      
      int result = 0;
         
      try {
    	  
         String query = " insert into Member "
                  + " values (seq_member_mno.nextval,?,?,?,?,?,sysdate) ";
            
         psmt = con.prepareStatement(query);
         psmt.setString(1, dto.getMid());
         psmt.setString(2, dto.getName());
         psmt.setString(3, dto.getPass());
         psmt.setString(4, dto.getEmail());
         psmt.setString(5, dto.getDomain());
            
         result = psmt.executeUpdate();
            
      }catch (Exception e) {
         e.printStackTrace();
      }
         
      return result;   
   }
   
   public int selectCount(OauthMemberDTO dto) {
	   
	   System.out.println("selectCount");
	   
	   System.out.println("dto.getEmail() : " + dto.getEmail());
	   System.out.println("dto.getDomain() : " + dto.getDomain());
	   
	   int result= 0;
	   
	   try {
		   
		   String query = "  select count(*) from member "
		   		+ " where email = ? "
		   		+ " and domain = ? ";
		   
		   psmt = con.prepareStatement(query);
		   psmt.setString(1, dto.getEmail());
		   psmt.setString(2, dto.getDomain());
		   rs = psmt.executeQuery();
		   
		   rs.next();
		   
		   if (rs.getInt(1) != 0) {
			   
			   System.out.println("rs.getInt(1) :" + rs.getInt(1));
			   
			   
			   result = rs.getInt(1);
		   }
	   }catch (Exception e) {
		System.out.println("회원 정보 확인 중 오류 발생");
		e.printStackTrace();
	}
	   
	   return result;
}
   
}

 

컨트롤러를 만든다.

로그인 화면 ← 실패 ← logincontroller → 성공 → index.jsp

회원 가입 → memberJoinController (dopost) → DAO → DB

Memberprocess를 쓰지 않고 controller를 사용한다.

 

 

서블릿에서 매핑한 주소를 어디서 어떻게 써야 하는지를 모르겠다.

 


SQL)

 

system.sql

create user oauth identified by 1234;

grant connect ,resource
to oauth;

 

ouarth.sql

create table member (
mno varchar2(10) primary key,
mid varchar2(10),
name varchar2(50),
pass varchar2(30),
email varchar2(50),
domain varchar2(30),
regidate date default sysdate
);

drop table member;

commit;

CREATE SEQUENCE seq_member_num
START WITH 1
INCREMENT BY 1
NOCACHE;

DELETE 
  FROM member
 WHERE mno = 4;
 
 
 -- 계속 회원가입 실패 오류가 떠서 직접 회원정보를 넣고 찍어봄. 
 -- 동일 이메일, 도메인으로 계속 회원가입을 시도한 후 지우고 커밋을 안해서 회원가입 실패 오류가 뜬 것이었다.
 
 select count(*) from member
 where email = '__@naver.com'
 and domain = 'facebook';
 
 commit;

 

이 날은 강사님 코드를 따라친게 아니라 직접 코드를 짜보는 실습 위주였는데 그래서인지 정말 내가 엉망이라고 많이 느꼈다.,

그리고 정리도 안돼서 더 엉망인데 주말동안 되돌아보며 내가 이 날 뭘 했는지 살펴보니 그냥 모델 2방식에 페이스북 로그인만 추가하면 되는 거였다. 계속 어렵게 느낀 이유는 내가 모델 2방식을 잘 이해하지 못해서였다.

배운 내용을 활용해 앞으로 여러 지도 API 사용해보기

 

간편인증방식 ; OAuth 2.0

                         사이트 ( Me ) ⇒ Client 

                                           주소 등록

사용자 ( Resource Owner ) → 회원가입 ( Resource Server )

 

사이트에서 사용자에게 정보를 사용해도 되는지 허락을 구하는 것 ⇒ 간편로그인

 

 

참고할 것 )

 

OAuth 2.0 개념과 동작원리

2022년 07월 13일에 작성한 글을 보충하여 새로 포스팅한 글이다. OAuth 등장 배경 우리의 서비스가 사용자를 대신하여 구글의 캘린더에 일정을 추가하거나, 페이스북, 트위터에 글을 남기는 기능을

hudi.blog

 

 

스크립트 기반으로 간편 로그인 기능 만들어보기

 

페이스북 디벨로퍼에 들어가 로그인 후 앱 만들기

 

 

문서 → 인증 → 로그인

 

웹 - Facebook 로그인 - 문서 - Meta for Developers

기본 자동 로그인 예 다음의 코드 샘플은 Javascript용 Facebook SDK를 웹페이지에 추가하고 SDK를 초기화하는 방법을 보여주고, Facebook에 로그인한 상태인 경우 이름과 이메일이 표시됩니다. Facebook에

developers.facebook.com

 

이클립스에서 Oauth 폴더 만들어서 해당 기능 구현

 

 

SDK 란?

SDK(Software Development Kit)는 특정 플랫폼이나 운영 체제에서 소프트웨어 응용 프로그램을 개발하기 위해 필요한 도구, 라이브러리, 코드 샘플, 문서 등을 포함한 패키지. SDK는 개발자들이 자신의 애플리케이션을 더 쉽게 만들 수 있도록 지원한다.

 

간편인증 만들기 순서

  1. sdk 로드
  2. sdk init
  3. 로그인
  4. 로그아웃

 

페이스북 디벨로퍼에서 로그인 기능을 그대로 가지고 온 코드이다. 실행하면 페이스북 로그인 버튼이 보이고 버튼을 눌러 로그인을 할 수 있다. 페이스북 아이디와 비밀번호로 다른 사이트(내가 만들 사이트)에서 로그인을 할 수 있게 만들어준다.

 

facebookOauth1.html

<!DOCTYPE html>
<html>
<head>
<title>Facebook Login JavaScript Example</title>
<meta charset="UTF-8">
</head>
<body>
<script>

  function statusChangeCallback(response) {  // Called with the results from FB.getLoginStatus().
    console.log('statusChangeCallback');
    console.log(response);                   // The current login status of the person.
    if (response.status === 'connected') {   // Logged into your webpage and Facebook.
	      testAPI();  
	    } else {                                 // Not logged into your webpage or we are unable to tell.
	      document.getElementById('status').innerHTML = 'Please log ' +
	        'into this webpage.';
	    }
  }


  function checkLoginState() {               // Called when a person is finished with the Login Button.
    FB.getLoginStatus(function(response) {   // See the onlogin handler
      statusChangeCallback(response);
    });
  }


  window.fbAsyncInit = function() {
    FB.init({
      appId      : 'appId',
      cookie     : true,                     // Enable cookies to allow the server to access the session.
      xfbml      : true,                     // Parse social plugins on this webpage.
      version    : 'v18.0'                   // Use this Graph API version for this call.
    });


    FB.getLoginStatus(function(response) {   // Called after the JS SDK has been initialized.
      statusChangeCallback(response);        // Returns the login status.
    });
  };
 
  function testAPI() {                      // Testing Graph API after login.  See statusChangeCallback() for when this call is made.
    console.log('Welcome!  Fetching your information.... ');
    FB.api('/me', function(response) {
      console.log('Successful login for: ' + response.name);
      document.getElementById('status').innerHTML =
        'Thanks for logging in, ' + response.name + '!';
    });
  }

</script>


<!-- The JS SDK Login Button -->

<fb:login-button scope="public_profile,email" onlogin="checkLoginState();">
</fb:login-button>

<div id="status">
</div>

<!-- Load the JS SDK asynchronously -->
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js"></script>
</body>
</html>

 

 

다른 사이트(내가 만들 사이트)에서 페이스북으로 로그인해 받아올 수 있는 값들 (fields)

  • id
  • first_name
  • last_name
  • middle_name
  • name
  • name_format
  • picture
  • short_name

이런 값들은 로그인 기능을 제공해준 api 사이트에서 확인할 수 있다.

 

graphDomain: “facebook” 어디로 도메인이 됐는지 확인.

로그인해서 정보를 받아오면 graphDomain: “facebook” 이라고 로그인한 도메인이 찍힌다.

네이버나 카카오로 하면 각 사이트의 이름이 찍힌다. (콘솔창에서 확인할 것!)

 

내가 만든 사이트에서 회원의 정보를 추가로 받아야 한다.

 

그대로 페이스북의 로그인 API를 가지고 오면 페이스북 로그인 버튼이 생기고, 버튼을 누르면 로그인창이 뜬다.

 

 

이젠 커스텀을 해본다.

로그인 버튼을 누르면 페이스북 로그인이 가능한 창이 뜨고 페이스북 로그인에서 토큰을 받아 내가 만든 사이트에 전달, 내가 만든 사이트에서 회원가입을 할 수 있도록 한다.

 

facebookOauth2.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script>

    function statusChangeCallback(response) {  // Called with the results from FB.getLoginStatus().
	    console.log('statusChangeCallback');
	    console.log(response);                   // The current login status of the person.
	    
	    if (response.status === 'connected') {   // Logged into your webpage and Facebook.
	      let domain = response.authResponse.graphDomain; // 도메인 정보 화면에 표시
	      document.querySelector('#authBtn').value = 'Logout';
	    
	    // 로그인이 성공했을 때 토큰
	    // '/me'를 쓰는 이유는 접속한 사람(나)의 정보를 가지고 오기 때문
	    // fields에서 받아올 수 있는 값을 확인하고 받아오고 싶은 값을 가지고 온다.
	      FB.api('/me',{fields:'email,name'}, function(response) {
	    	  // 로그인 성공했을 때 화면에 보여줄 메세지
	    	  // response 응답
	          document.querySelector('#name').innerHTML = response.name + "님 로그인 성공(" + response.email + ")" + domain;
	    	  window.location.href = 'member.jsp?name=' + response.name + "&email=" + response.email + "&domain=" + domain;
	        });
	    
	    } else {                                 // Not logged into your webpage or we are unable to tell.
	      
	      document.querySelector('#authBtn').value = 'Login';
	    // 로그아웃 후에는 이름, 이메일 정보가 안보이게 처리
	      document.querySelector('#name').innerHTML = "";
	        
	    }
    }
  
	window.fbAsyncInit = function() {
	    FB.init({
	      appId      : 'appId',        // 페이스북에서 발급받은 내 앱ID을 넣어야 한다.
	      cookie     : true,                     // Enable cookies to allow the server to access the session.
	      xfbml      : true,                     // Parse social plugins on this webpage.
	      version    : 'v18.0'                   // Use this Graph API version for this call.
	    });
	    
	    FB.getLoginStatus(function(response) {   // Called after the JS SDK has been initialized.
	        statusChangeCallback(response);        // Returns the login status.
      });
    };
</script>

   <!-- 로그인과 로그아웃을 하는 버튼 -->
   <input type="button" id="authBtn" value="페이스북 로그인" onclick="
         if(this.value === 'Login'){
            //로그아웃 상태
            FB.login(function(response){
                 // handle the response 
                 console.log('logout => response');
                 statusChangeCallback(response);
            });
         }else{
            //로그인 상태
            FB.logout(function(response) {
                  // Person is now logged out
                  console.log('login => response');
                  statusChangeCallback(response);
            });
         }
      ">
      
	<span id="name"></span>

<!-- sdk 로드  -->
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js"></script>
</body>
</html>

쿠키를 지우지 않아서 다시 시도했을 때 이런 창이 뜨지만 로그인 버튼을 눌렀을 때 연동되는 페이스북 페이지에서도 내 쿠키를 수집하고 있다는 것을 알 수 있다.

추가로 아래 코드를 통해

이렇게 이름과 이메일과 같은 fields 로 불러온 값을 회원가입 페이지에서 받아올 수 있게 만든다.

 

 

 

 

SQL 디벨로퍼에서 회원가입을 통해 회원정보를 받을 테이블을 만든다.

 

SQL)

테이블명 : member

회원가입 추가 정보 입력 창 만들기

번호 : 시퀀스

아이디,

이름 : 메타 이름

비밀번호,

이메일 : 메타이메일

도메인 : 메타도메인

가입일

create table member (
num varchar2(10) primary key,
mid varchar2(10),
name varchar2(50),
pass varchar2(30),
email varchar2(50),
domain varchar2(30),
regidate date default sysdate
);

drop table member;

commit;

CREATE SEQUENCE seq_member_num
START WITH 1
INCREMENT BY 1
NOCACHE;

 

 

 

회원정보를 입력 받을 폼을 만든다.

member.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
   String name = request.getParameter("name");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
   #wrap {
      width:800px;
      margin : 20px auto;
   }
   
   input[type=text],input[type=password] {
      width:95%;
   }
</style>
</head>
<body>
   <div id="wrap">
      <h1>회원가입페이지</h1>
      <form method="post" action="memberProcess.jsp">
         <table border="1" width="100%">
            <colgroup>
               <col width="30%" />
               <col width="*" />
            </colgroup>
            <tr>
               <td>아이디</td>
               <td>
                  <input type="text" name="id" />
               </td>
            <tr>
            <tr>
               <td>비밀번호</td>
               <td>
                  <input type="password" name="pass" />
               </td>
            <tr>
            <tr>
               <td>이름</td>
               <td>
                  <input type="text" name="name" value="${param.name }" />
               </td>
            <tr>
            
            <tr>
               <td>이메일</td>
               <td>
                  <input type="text" name="email" value="${param.email }" />
               </td>
            <tr>
            <tr>
               <td>도메인</td>
               <td>
                  <input type="text" name="domain" value="${param.domain }"/>
               </td>
            <tr>
         </table>
         <input type="submit" value="회원가입" />
         <input type="reset" value="취소">
      </form>
   </div>
</body>
</html>

 

DB와 연결할 DTO와 DAO를 만든다.

 

OauthMemberDTO.java

package oauth.member;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class OauthMemberDTO {
	private String mno;
	private String mid;
	private String name;
	private String pass;
	private String email;
	private String domain;
	private String regidate;
}

 

OauthMemberDAO.java

package oauth.member;
import common.JDBConnect;

public class OauthMemberDAO extends JDBConnect{   
   public int insertmember(OauthMemberDTO dto) {
      
      int result = 0;
         
      String query = " insert into Member "
            + " (mno,mid,name,pass,email,domain) "
            + " values (seq_member_num.nextval,?,?,?,?,?) ";
         
      try {
            
         psmt = con.prepareStatement(query);
         psmt.setString(1, dto.getMid());
         psmt.setString(2, dto.getName());
         psmt.setString(3, dto.getPass());
         psmt.setString(4, dto.getEmail());
         psmt.setString(5, dto.getDomain());
            
         result = psmt.executeUpdate();
            
      }catch (Exception e) {
         e.printStackTrace();
      }
         
      return result;   
   }
}

 

memberProcess.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="oauth.member.OauthMemberDTO"%>
<%@page import="oauth.member.OauthMemberDAO"%>
<%@ page import="utils.JSFunction"%>


<%
   OauthMemberDTO dto = new OauthMemberDTO();
   
String mid = request.getParameter("mid");
String pass = request.getParameter("pass");
String email = request.getParameter("email");
String domain = request.getParameter("domain");
String name = request.getParameter("name");
   
   System.out.print(mid);
   System.out.print(pass);
   System.out.print(email);
   System.out.print(domain);
   System.out.print(name);
   
   dto.setMid(mid);
   dto.setPass(pass);
   dto.setEmail(email);
   dto.setName(name);
   dto.setDomain(domain);
   
   OauthMemberDAO dao = new OauthMemberDAO();
   
   int result = dao.insertmember(dto);
   
   if(result > 0){
      response.sendRedirect("fackbookOauth2.html");
   }else{
      JSFunction.alertBack("회원가입에 실패하였습니다.", out);
   }
   
%>

 

이렇게 만들었을 때 값은 잘 받아왔지만 결과적으로 회원가입이 되지 않았다.. 뭐가 문제일까

 

ORA-02289: 시퀀스가 존재하지 않습니다. 오류가 떠 확인해보니 시퀀스는 잘 만들어져 있었다..

알 수가 없다. 정말

 

알고 보니 커밋의 문제 였다. 자나깨나 커밋을 잘 하자.

 

 


 

오늘도 놀라운 일이 벌어졌다..

페북 계정 로그인하려다 비번을 계속 틀려서

 

 

이렇게 되었음!! 그래서 api를 읽어올 수가 없다ㅠ

요청 검토까지 하루가 걸린다고 했으니.. 내일이나 주말에 다시 해보기

다행히 동료분이 아이디와 비번을 빌려주셔서 겨우 했는데 잘 안됨 (코드문제)

 

일단 저렇게 페이스북 로그인을 못하게 될 경우에는 PC로는 재고요청을 할 방법이 없고 

모바일 앱을 다운받아 진짜 사람인지 안면인식 영상을 30초 이상 찍어서 페이스북 측에 보내야 한다..

그럼 다시 로그인을 시도했을 때 저렇게 재고요청이 완료되었다는 메세지가 뜨고

2~3일 정도(최대 일주일) 걸려 계정 비활성화가 풀린다. 

 

코드 오류는 해결되어 따로 올릴 예정.

어제에 이어 네이버 API 를 따 와 블로그 검색을 하는 기능을 만든다.

 

SearchAPI.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- 제이쿼리  -->
<script>
	$(function() {
		$("#searchBtn").click(function() {
			
			
			
			$.ajax({
				url : "../NaverSearchAPI.do",
				type : "get",
				data : {
					keyword : $("#keyword").val(),
					startNum : $("#startNum option:selected").val()
				},
				dataType : "json",
				success : sucFuncJson,
				error : errFunc
			});
		});
	});

	// 성공 (success 처리를 외부로 빼는 방법)
	function sucFuncJson(d) {
		// 제이슨 형식으로 결과를 받아온다
		// 어떤 식으로, 어떤 속성을 포함해서 값을 넘겨주는지 알아오기 위해 콘솔 로그 찍어보기
		console.log(d);

		let str = "";
		$.each(d.items, function(index, item) {
			str += "<ul>";
			str += " <li>" + (index + 1) + "</li>";
			str += " <li>" + item.title +"</li>";
			str += " <li>" + item.description  +"</li>";
			str += " <li>" + item.bloggername  +"</li>";
			str += " <li>" + item.bloggerlink +"</li>";
			str += " <li><a href ='" + item.link + "'>" + item.link + "</a></li>";
			str += " <li>" + item.postdata +"</li>";
			str += "</ul>";
		});
		$("#searchResult").html(str);
	}

	// 실패
	function errFunc(e) {
		alert("실패 : " + e.status);
	}
</script>
<style>
ul {
border: 2px solid #cccccc
}
</style>
</head>
<body>
	<div>
		<form id="searchFrm">
			<select id="startNum">
				<option value="1">1페이지</option>
				<option value="11">2페이지</option>
				<option value="21">3페이지</option>
				<option value="31">4페이지</option>
				<option value="41">5페이지</option>
			</select> 
			<input type="text" id="keyword" placeholder="검색어를 입력하세요">
			<button type="button" id="searchBtn">검색 요청</button>
		</form>
	</div>
	<div id="searchResult">
	여기에 검색 결과가 출력됩니다.
	</div>
</body>
</html>

 

SearchAPI.java (내가 쓴 이상하게 동작되는 코드 - 비교를 위해 남겨 놓음)

int startNum = 0;
        String text = null;
        try {
        	
        	startNum = Integer.parseInt(req.getParameter("startNum"));
        	String searchText = req.getParameter("keyword");
            text = URLEncoder.encode("searchText", "UTF-8");
            
            System.out.println(searchText);
            
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("검색어 인코딩 실패",e);
        }
        
        String apiURL = "https://openapi.naver.com/v1/search/blog?query=" + text
        		+ "&display=10&start=" + startNum;    // JSON 결과
        
        Map<String, String> requestHeaders = new HashMap<>();
        requestHeaders.put("X-Naver-Client-Id", clientId);
        requestHeaders.put("X-Naver-Client-Secret", clientSecret);
        String responseBody = get(apiURL,requestHeaders);


        System.out.println(responseBody);
        
        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().write(responseBody);
        
	}
	
    private static String get(String apiUrl, Map<String, String> requestHeaders){
        HttpURLConnection con = connect(apiUrl);
        try {
            con.setRequestMethod("GET");
            for(Map.Entry<String, String> header :requestHeaders.entrySet()) {
                con.setRequestProperty(header.getKey(), header.getValue());
            }


            int responseCode = con.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 호출
                return readBody(con.getInputStream());
            } else { // 오류 발생
                return readBody(con.getErrorStream());
            }
        } catch (IOException e) {
            throw new RuntimeException("API 요청과 응답 실패", e);
        } finally {
            con.disconnect();
        }
    }
    
    private static HttpURLConnection connect(String apiUrl){
        try {
            URL url = new URL(apiUrl);
            return (HttpURLConnection)url.openConnection();
        } catch (MalformedURLException e) {
            throw new RuntimeException("API URL이 잘못되었습니다. : " + apiUrl, e);
        } catch (IOException e) {
            throw new RuntimeException("연결이 실패했습니다. : " + apiUrl, e);
        }
    }
    
    private static String readBody(InputStream body){
        InputStreamReader streamReader = new InputStreamReader(body);


        try (BufferedReader lineReader = new BufferedReader(streamReader)) {
            StringBuilder responseBody = new StringBuilder();


            String line;
            while ((line = lineReader.readLine()) != null) {
                responseBody.append(line);
            }


            return responseBody.toString();
        } catch (IOException e) {
            throw new RuntimeException("API 응답을 읽는 데 실패했습니다.", e);
        }
    }

}

 

 

 

오픈 API 활용

 

1. 공공데이터포탈 사이트 접속

2. 로그인 후 원하는 데이터 찾아 오픈 API 탭에서 데이터 활용 요청

3. 마이페이지에서 신청한 데이터 인증키 설정하기

4. 일반 인증키 (Decoding) 을 긁어와 ApikeyAuth2에 넣고 설정을 누르면 인증키가 발급된다.

 

5. API 목록에서 두번째 탭에서 openAPI 활용을 누르면 데이터 목록이 제이슨형식으로 목록이 나온다.

우리는 거기서 Request URL을 사용한다. 인증키까지 포함되어 있는 주소다.

그 주소를 그대로 옮겨 인터넷 창에 치면

 

데이터를 받을 수 있다.

좀 더 정리 된 모습으로 보기위해 구글스토어에서 json viewer를 다운 받아 보았다.

 

 

 

미션) 약국 정보를 네이버 블로그 검색했던 것처럼 화면에 목록을 만들어 뿌려보기

검색은 안되는데 나중에 구현이 될까 싶어 일단 창을 저렇게 두었다.

지금은 검색 요청 버튼을 누르면 그냥 광진구 약국 목록이 뜬다.

 

 

OpenView.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- 제이쿼리  -->
<script>
	$(function() {
		$("#searchBtn").click(function() {
			
			
			
			$.ajax({
				url : "https://api.odcloud.kr/api/15052413/v1/uddi:4f0d5c8a-5b2b-4097-b420-d845315aa993?page=1&perPage=10&serviceKey=SLdtAgHWosK5tvRVL%2Bw87WA8CQz%2FvJIV8hmrV5Yocqqqwu21Ft5IhVLQX1aF6iY5k2pj3Xf%2BztwhMAedzEeF1A%3D%3D",
				type : "get",
				data : {
					keyword : $("#keyword").val(),
					startNum : $("#startNum option:selected").val()
				},
				dataType : "json",
				success : sucFuncJson,
				error : errFunc
			});
		});
	});

	// 성공 (success 처리를 외부로 빼는 방법)
	function sucFuncJson(d) {
		// 제이슨 형식으로 결과를 받아온다
		// 어떤 식으로, 어떤 속성을 포함해서 값을 넘겨주는지 알아오기 위해 콘솔 로그 찍어보기
		console.log(d);

		let str = "";
		$.each(d.data, function(index, item) {
			str += "<ul>";
			// str += " <li>" + (index + 1) + "</li>";
			str += " <li>" + item.순번 +"</li>";
			str += " <li>" + item.약국명칭  +"</li>";
			str += " <li>" + item['약국소재지(도로명)']  +"</li>";
			str += " <li>" + item['약국우편번호(도로명)']+"</li>";
			str += "</ul>";
		});
		$("#searchResult").html(str);
	}

	// 실패
	function errFunc(e) {
		alert("실패 : " + e.status);
	}
</script>
<style>
ul {
border: 2px solid #cccccc
}
</style>
</head>
<body>
	<div>
		<form id="searchFrm">
			<select id="startNum">
				<option value="1">1페이지</option>
				<option value="11">2페이지</option>
				<option value="21">3페이지</option>
				<option value="31">4페이지</option>
				<option value="41">5페이지</option>
			</select> 
			<input type="text" id="keyword" placeholder="검색어를 입력하세요">
			<button type="button" id="searchBtn">검색 요청</button>
		</form>
	</div>
	<div id="searchResult">
	여기에 검색 결과가 출력됩니다.
	</div>
</body>
</html>

처음에는 제이슨 형식을 작성할때 자바스크립트로 써줘야 한다는 것을 잊고

		str += " <li>" + item['약국소재지(도로명)']  +"</li>";
		str += " <li>" + item['약국우편번호(도로명)']+"</li>";

부분을 그냥

		str += " <li>" + item약국소재지_도로명  +"</li>";
		str += " <li>" + item약국우편번호_도로명)+"</li>";

이렇게 썼는데 값을 못읽어왔다.

아무래도 이상한 것 같아 다시 찾아오니 위 처럼 쓰는 것이 맞았고 그래야 값도 null 이 아니게 잘 나온다.

다 하고 보니 강사님은 약국전화번호까지 나온 데이터를 가지고 와 있었다.

전화번호를 넣기 위해 코드를 다시 수정했다.

 

 

수정 후 화면)

 

OpenView.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- 제이쿼리  -->
<script>
	$(function() {
		$("#searchBtn").click(function() {
			
			
			
			$.ajax({
				url : "https://api.odcloud.kr/api/15052413/v1/uddi:816ab9e3-dafe-4701-839b-c0c4d53adffe_201902261655?page=1&perPage=10&serviceKey=SLdtAgHWosK5tvRVL%2Bw87WA8CQz%2FvJIV8hmrV5Yocqqqwu21Ft5IhVLQX1aF6iY5k2pj3Xf%2BztwhMAedzEeF1A%3D%3D",
				type : "get",
				data : {
					keyword : $("#keyword").val(),
					startNum : $("#startNum option:selected").val()
				},
				dataType : "json",
				success : sucFuncJson,
				error : errFunc
			});
		});
	});

	// 성공 (success 처리를 외부로 빼는 방법)
	function sucFuncJson(d) {
		// 제이슨 형식으로 결과를 받아온다
		// 어떤 식으로, 어떤 속성을 포함해서 값을 넘겨주는지 알아오기 위해 콘솔 로그 찍어보기
		console.log(d);

		let str = "";
		$.each(d.data, function(index, item) {

			str += "<ul>";
			// str += " <li>" + (index + 1) + "</li>";
			str += " <li>순번: " + item.순번 + "</li>";
			str += " <li>약국명칭: " + item.약국명칭 + "</li>";
			str += " <li>약국소재지: " + item['약국소재지(도로명)'] + "</li>";
			str += " <li>약국우편번호: " + item.약국우편번호 + "</li>";
			str += " <li>약국전화번호: " + item.약국전화번호 + "</li>";
			str += "</ul>";
		});
		$("#searchResult").html(str);
	}

	// 실패
	function errFunc(e) {
		alert("실패 : " + e.status);
	}
</script>
<style>
ul {
border: 2px solid #cccccc
}
</style>
</head>
<body>
	<div>
		<form id="searchFrm">
			<select id="startNum">
				<option value="1">1페이지</option>
				<option value="11">2페이지</option>
				<option value="21">3페이지</option>
				<option value="31">4페이지</option>
				<option value="41">5페이지</option>
			</select> 
			<input type="text" id="keyword" placeholder="검색어를 입력하세요">
			<button type="button" id="searchBtn">검색 요청</button>
		</form>
	</div>
	<div id="searchResult">
	여기에 검색 결과가 출력됩니다.
	</div>
</body>
</html>

 

			str += " <li>약국소재지: " + item['약국소재지(도로명)'] + "</li>";
			str += " <li>약국우편번호: " + item.약국우편번호 + "</li>";

 

  • 여기서는 약국우편번호가 이렇게 해야 읽혔다.. 뭐지

 

아하 헷갈린 이유가 아까 api에서는 우편번호(도로명) 이렇게 되어있었고 지금 수정한 api는 그냥 우편번호 라고만 적혀있었는데 내가 그걸 잘 보지 않고 혼자 헤맨 것!

제발 정신을 차리고 변수명을 좀 잘 보자!!!!!

 

  • 이와 같은 차이는 JSON 데이터의 키 이름이 일반적인 변수명 규칙을 따르는지 여부에 따라 다릅니다.

1. 일반적인 변수명 규칙을 따르는 경우: 키 이름이 공백, 특수문자 등을 포함하지 않으면 점 표기법을 사용할 수 있습니다.

str += " <li>순번: " + item.순번 + "</li>";
str += " <li>약국명칭: " + item.약국명칭 + "</li>";
str += " <li>약국전화번호: " + item.약국전화번호 + "</li>";

2. 일반적인 변수명 규칙을 따르지 않는 경우: 키 이름에 공백, 특수문자, 괄호 등이 포함되면 대괄호 표기법을 사용해야 합니다.

str += " <li>약국소재지: " + item['약국소재지(도로명)'] + "</li>";
str += " <li>약국우편번호: " + item['약국우편번호'] + "</li>";

따라서 약국소재지(도로명)과 같은 키는 대괄호 표기법을 사용해야 하고, 순번, 약국명칭, 약국전화번호와 같은 키는 점 표기법을 사용할 수 있습니다.

전체 코드를 일관되게 작성한 예시는 다음과 같습니다:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>약국 정보 검색</title>
<script src="<https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js>"></script>
<!-- 제이쿼리  -->
<script>
	$(function() {
		$("#searchBtn").click(function() {
			$.ajax({
				url: "<https://api.odcloud.kr/api/15052413/v1/uddi:816ab9e3-dafe-4701-839b-c0c4d53adffe_201902261655?page=1&perPage=10&serviceKey=SLdtAgHWosK5tvRVL%2Bw87WA8CQz%2FvJIV8hmrV5Yocqqqwu21Ft5IhVLQX1aF6iY5k2pj3Xf%2BztwhMAedzEeF1A%3D%3D>",
				type: "get",
				data: {
					keyword: $("#keyword").val(),
					startNum: $("#startNum option:selected").val()
				},
				dataType: "json",
				success: sucFuncJson,
				error: errFunc
			});
		});
	});

	// 성공 (success 처리를 외부로 빼는 방법)
	function sucFuncJson(d) {
		// JSON 형식으로 결과를 받아온다
		console.log(d);

		let str = "";
		$.each(d.data, function(index, item) {
			str += "<ul>";
			str += " <li>순번: " + item.순번 + "</li>";
			str += " <li>약국명칭: " + item.약국명칭 + "</li>";
			str += " <li>약국소재지: " + item['약국소재지(도로명)'] + "</li>";
			str += " <li>약국우편번호: " + item['약국우편번호'].replace("'", "") + "</li>";
			str += " <li>약국전화번호: " + item.약국전화번호 + "</li>";
			str += "</ul>";
		});
		$("#searchResult").html(str);
	}

	// 실패
	function errFunc(e) {
		alert("실패 : " + e.status);
	}
</script>
<style>
ul {
	border: 2px solid #cccccc;
	list-style-type: none;
	padding: 10px;
	margin: 5px;
}
li {
	margin: 5px 0;
}
</style>
</head>
<body>
	<div>
		<form id="searchFrm">
			<select id="startNum">
				<option value="1">1페이지</option>
				<option value="11">2페이지</option>
				<option value="21">3페이지</option>
				<option value="31">4페이지</option>
				<option value="41">5페이지</option>
			</select>
			<input type="text" id="keyword" placeholder="검색어를 입력하세요">
			<button type="button" id="searchBtn">검색 요청</button>
		</form>
	</div>
	<div id="searchResult">
		여기에 검색 결과가 출력됩니다.
	</div>
</body>
</html>

 

 

다르게 푸는 방법

pharmacy.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>약국 정보 검색</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- 제이쿼리  -->
<script>
	$(function() {
		var url = "https://api.odcloud.kr/api/15052413/v1/uddi:816ab9e3-dafe-4701-839b-c0c4d53adffe_201902261655?page=1&perPage=10&serviceKey=SLdtAgHWosK5tvRVL%2Bw87WA8CQz%2FvJIV8hmrV5Yocqqqwu21Ft5IhVLQX1aF6iY5k2pj3Xf%2BztwhMAedzEeF1A%3D%3D";
		$.getJSON(url, function(result) {
			var data = result.data;
			var table = $("<table />").addClass("pharmacy-table");
			var header = $("<tr />").append(
				$("<th />").text("순번"),
				$("<th />").text("약국명칭"),
				$("<th />").text("약국소재지"),
				$("<th />").text("약국우편번호"),
				$("<th />").text("약국전화번호")
			);
			table.append(header);
			$.each(data, function(i, d) {
				var row = $("<tr />").append(
					$("<td />").text(d["순번"]),
					$("<td />").text(d["약국명칭"]),
					$("<td />").text(d["약국소재지(도로명)"]),
					$("<td />").text(d["약국우편번호"].replace("'", "")),
					$("<td />").text(d["약국전화번호"])
				);
				table.append(row);
			});
			$("#searchResult").html(table);
		});
	});
</script>
<style>
.pharmacy-table {
	width: 100%;
	border-collapse: collapse;
}

.pharmacy-table th, .pharmacy-table td {
	border: 1px solid #cccccc;
	padding: 8px;
	text-align: left;
}

.pharmacy-table th {
	background-color: #f2f2f2;
}

.pharmacy-table tr:nth-child(even) {
	background-color: #f9f9f9;
}

.pharmacy-table tr:hover {
	background-color: #f1f1f1;
}
</style>
</head>
<body>
	<div id="searchResult">
		<!-- 검색 결과가 여기에 표시됩니다. -->
	</div>
</body>
</html>

이건 gpt가 만들어준 정리된 화면이다. 정말 핸섬..

 

 

 

MapAPI

 

이제 이 정보를 지도로 가지고 오기.

카카오 개발자센터에서

내 어플리케이션 만들기 하고

플랫폼에 내 로컬호스트 등록하기. (중요)

 

 그 다음 mapapi로 들어가 마음에 드는 지도를 고르고 지도의 코드를 복붙만 하면 된다.

코드를 복붙하고 발급 받은 key를 넣어 실행하면 내 로컬호스트 주소로도 지도가 뜨는 것을 확인할 수 있다.

 

 

 

 

마커 생성하기

 

KakaoMap.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>마커 생성하기</title>
    
</head>
<body>
<div id="map" style="width:100%;height:350px;"></div>

<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=436ce201a79136708b878547edc3bc6c"></script>
<script>
var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
    mapOption = { 
        center: new kakao.maps.LatLng(37.55408884685698, 126.93578377600151), // 지도의 중심좌표
        level: 3 // 지도의 확대 레벨
    };

var map = new kakao.maps.Map(mapContainer, mapOption); // 지도를 생성합니다

// 마커가 표시될 위치입니다 
var markerPosition  = new kakao.maps.LatLng(37.55408884685698, 126.93578377600151); 

// 마커를 생성합니다
var marker = new kakao.maps.Marker({
    position: markerPosition
});

// 마커가 지도 위에 표시되도록 설정합니다
marker.setMap(map);

// 아래 코드는 지도 위의 마커를 제거하는 코드입니다
// marker.setMap(null);    
</script>
</body>
</html>

 

 

주소로 장소 표시하기

 

KakaoMap2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>주소로 장소 표시하기</title>
    
</head>
<body>
<p style="margin-top:-12px">
    <em class="link">
        <a href="javascript:void(0);" onclick="window.open('http://fiy.daum.net/fiy/map/CsGeneral.daum', '_blank', 'width=981, height=650')">
            혹시 주소 결과가 잘못 나오는 경우에는 여기에 제보해주세요.
        </a>
    </em>
</p>
<div id="map" style="width:100%;height:350px;"></div>

<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=436ce201a79136708b878547edc3bc6c&libraries=services"></script>
<script>
var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
    mapOption = {
        center: new kakao.maps.LatLng(37.55408884685698, 126.93578377600151), // 지도의 중심좌표
        level: 3 // 지도의 확대 레벨
    };  

// 지도를 생성합니다    
var map = new kakao.maps.Map(mapContainer, mapOption); 

// 주소-좌표 변환 객체를 생성합니다
var geocoder = new kakao.maps.services.Geocoder();

// 주소로 좌표를 검색합니다
geocoder.addressSearch('서울 마포구 서강로 136 아이비타워', function(result, status) {

    // 정상적으로 검색이 완료됐으면 
     if (status === kakao.maps.services.Status.OK) {

        var coords = new kakao.maps.LatLng(result[0].y, result[0].x);

        // 결과값으로 받은 위치를 마커로 표시합니다
        var marker = new kakao.maps.Marker({
            map: map,
            position: coords
        });

        // 인포윈도우로 장소에 대한 설명을 표시합니다
        var infowindow = new kakao.maps.InfoWindow({
            content: '<div style="width:150px;text-align:center;padding:6px 0;">코리아it아카데미</div>'
        });
        infowindow.open(map, marker);

        // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다
        map.setCenter(coords);
    } 
});    
</script>
</body>
</html>

 

 

광진구 약국 정보를 지도에 마커로 표시하기

 

PharmarcyMap.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>주소로 장소 표시하기</title>
    <style>
       table,th,tr,td {
          border:2px solid #cccccc
       }
   </style>
</head>
<body>
<p style="margin-top:-12px">
    <em class="link">
        <a href="javascript:void(0);" onclick="window.open('http://fiy.daum.net/fiy/map/CsGeneral.daum', '_blank', 'width=981, height=650')">
            혹시 주소 결과가 잘못 나오는 경우에는 여기에 제보해주세요.
        </a>
    </em>
</p>
<div id="map" style="width:100%;height:350px;"></div>

<h1> ※ 광진구 약국 정보 </h1>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <!-- 제이쿼리  -->
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=436ce201a79136708b878547edc3bc6c&libraries=services"></script>
<script>


// 약국정보 테이블 표시 start

var url = "https://api.odcloud.kr/api/15052413/v1/uddi:816ab9e3-dafe-4701-839b-c0c4d53adffe_201902261655?page=1&perPage=10&serviceKey=SLdtAgHWosK5tvRVL%2Bw87WA8CQz%2FvJIV8hmrV5Yocqqqwu21Ft5IhVLQX1aF6iY5k2pj3Xf%2BztwhMAedzEeF1A%3D%3D";
$.getJSON(url, function(result) {  // getJSON 시작
   
        var data = result.data;
        console.log(data);
        var tb = $("<table />")
        $(tb).append("<th>순번</th><th>약국명칭</th><th>약국소재지(도로명)</th><th>약국우편번호</th><th>약국전화번호</th>");
        $.each(data, function(i, d) {
          var row = $("<tr />").append(
                        $("<td />").text(d["순번"]),
                          $("<td />").text(d["약국명칭"]),
                          $("<td />").text(d["약국소재지(도로명)"]),
                          $("<td />").text(d["약국우편번호"]),
                          $("<td />").text(d["약국전화번호"])                                 
                      );
          tb.append(row);
        });
        $("body").append(tb);
   


      // 기존 라이브러 코드 start

      var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
          mapOption = {
              center: new kakao.maps.LatLng(33.450701, 126.570667), // 지도의 중심좌표
              level: 7 // 지도의 확대 레벨
          };  
      
      // 지도를 생성합니다    
      var map = new kakao.maps.Map(mapContainer, mapOption); 
      
      // 주소-좌표 변환 객체를 생성합니다
      var geocoder = new kakao.maps.services.Geocoder();
      
      // 주소로 좌표를 검색합니다
      $.each(data, function(i, d) {
	      geocoder.addressSearch(d["약국소재지(도로명)"], function(result, status) {
	      
	          // 정상적으로 검색이 완료됐으면 
	           if (status === kakao.maps.services.Status.OK) {
	      
	              var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
	      
	              // 결과값으로 받은 위치를 마커로 표시합니다
	              var marker = new kakao.maps.Marker({
	                  map: map,
	                  position: coords
	              });
	      
	              // 인포윈도우로 장소에 대한 설명을 표시합니다
	              var infowindow = new kakao.maps.InfoWindow({
	                  content: '<div style="width:150px;text-align:center;padding:6px 0;">' + d["약국명칭"] + '</div>'
	              });
	              infowindow.open(map, marker);
	      
	              // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다
	              map.setCenter(coords);
	          } 
	      }); 
	      
	      // 기존 라이브러 코드 end
      });

}); // getJSON 끝

// 약국정보 테이블 표시 end

</script>
</body>
</html>

 

 

약국의 로드뷰 읽어오기

 

PharmarcyMap2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>주소로 장소 표시하기</title>
    <style>
       table, th, tr, td {
          border: 2px solid #cccccc;
          padding: 5px;
          text-align: left;
       }
       th {
          background-color: #f2f2f2;
       }
       td {
          cursor: pointer;
       }
       
       .address:hover {
       color : skyblue;
       }
       
   </style>
</head>
<body>
<p style="margin-top:-12px">
    <em class="link">
        <a href="javascript:void(0);" onclick="window.open('http://fiy.daum.net/fiy/map/CsGeneral.daum', '_blank', 'width=981, height=650')">
            혹시 주소 결과가 잘못 나오는 경우에는 여기에 제보해주세요.
        </a>
    </em>
</p>

<div id="map" style="width:100%;height:350px;"></div> <!-- 지도표시영역 -->
<div id="roadview" style="width:100%;height:300px;"></div> <!-- 로드뷰를 표시할 div 입니다 -->


<h1> ※ 광진구 약국 정보 </h1>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <!-- 제이쿼리  -->
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=436ce201a79136708b878547edc3bc6c&libraries=services"></script>
<script>


// 약국정보 테이블 표시 start

var url = "https://api.odcloud.kr/api/15052413/v1/uddi:816ab9e3-dafe-4701-839b-c0c4d53adffe_201902261655?page=1&perPage=10&serviceKey=SLdtAgHWosK5tvRVL%2Bw87WA8CQz%2FvJIV8hmrV5Yocqqqwu21Ft5IhVLQX1aF6iY5k2pj3Xf%2BztwhMAedzEeF1A%3D%3D";
$.getJSON(url, function(result) {  // getJSON 시작
   
        var data = result.data;
        console.log(data);
        var tb = $("<table />")
        $(tb).append("<th>순번</th><th>약국명칭</th><th>약국소재지(도로명)</th><th>약국우편번호</th><th>약국전화번호</th>");
        $.each(data, function(i, d) {
          var row = $("<tr />").append(
                        $("<td />").text(d["순번"]),
                          $("<td />").text(d["약국명칭"]),
                          $("<td class='address' />").text(d["약국소재지(도로명)"]).data("address", d["약국소재지(도로명)"]),
                          $("<td />").text(d["약국우편번호"]),
                          $("<td />").text(d["약국전화번호"])                                 
                      );
          tb.append(row);
        });
        $("body").append(tb);
   


      // 기존 라이브러 코드 start

      var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
          mapOption = {
              center: new kakao.maps.LatLng(33.450701, 126.570667), // 지도의 중심좌표
              level: 7 // 지도의 확대 레벨
          };  
      
      // 지도를 생성합니다    
      var map = new kakao.maps.Map(mapContainer, mapOption); 
      
      // 주소-좌표 변환 객체를 생성합니다
      var geocoder = new kakao.maps.services.Geocoder();
      
      // 주소로 좌표를 검색합니다
      $.each(data, function(i, d) {
	      geocoder.addressSearch(d["약국소재지(도로명)"], function(result, status) {
	      
	          // 정상적으로 검색이 완료됐으면 
	           if (status === kakao.maps.services.Status.OK) {
	      
	              var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
	      
	              // 결과값으로 받은 위치를 마커로 표시합니다
	              var marker = new kakao.maps.Marker({
	                  map: map,
	                  position: coords
	              });
	      
	              // 인포윈도우로 장소에 대한 설명을 표시합니다
	              var infowindow = new kakao.maps.InfoWindow({
	                  content: '<div style="width:150px;text-align:center;padding:6px 0;">' + d["약국명칭"] + '</div>'
	              });
	              infowindow.open(map, marker);
	      
	              // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다
	              map.setCenter(coords);
	          } 
	      }); 
	      
	      // 기존 라이브러 코드 end
      });

   // 주소 클릭 시 로드뷰 표시
      $(document).on("click", ".address", function() {
          var address = $(this).data("address");
          geocoder.addressSearch(address, function(result, status) {
              if (status === kakao.maps.services.Status.OK) {
                  var position = new kakao.maps.LatLng(result[0].y, result[0].x);
                  var roadviewContainer = document.getElementById('roadview'); //로드뷰를 표시할 div
                  var roadview = new kakao.maps.Roadview(roadviewContainer); //로드뷰 객체
                  var roadviewClient = new kakao.maps.RoadviewClient(); //좌표로부터 로드뷰 파노ID를 가져올 로드뷰 helper객체

                  // 특정 위치의 좌표와 가까운 로드뷰의 panoId를 추출하여 로드뷰를 띄운다.
                  roadviewClient.getNearestPanoId(position, 50, function(panoId) {
                      roadview.setPanoId(panoId, position); //panoId와 중심좌표를 통해 로드뷰 실행
                      $("#roadview").show();
                  });
              }
          });
      });

}); // getJSON 끝

// 약국정보 테이블 표시 end

</script>
</body>

 

PharmacyRodeViewMap3.jsp ( 로드뷰만 나온 ver.)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>로드뷰 생성하기</title>

</head>
<style>
table, th, tr, td {
	border: 2px solid #cccccc;
	padding: 5px;
	text-align: left;
}

.address:hover {
	color: skyblue;
}
</style>
<body>
	<!-- 로드뷰를 표시할 div 입니다 -->
	<div id="roadview" style="width: 100%; height: 300px;"></div>

	<script
		src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <!-- 제이쿼리  -->
	<script type="text/javascript"
		src="//dapi.kakao.com/v2/maps/sdk.js?appkey=436ce201a79136708b878547edc3bc6c&libraries=services"></script>
	<script>
var roadviewContainer = document.getElementById('roadview'); //로드뷰를 표시할 div
var roadview = new kakao.maps.Roadview(roadviewContainer); //로드뷰 객체
var roadviewClient = new kakao.maps.RoadviewClient(); //좌표로부터 로드뷰 파노ID를 가져올 로드뷰 helper객체

var position = new kakao.maps.LatLng(33.450701, 126.570667);

// 특정 위치의 좌표와 가까운 로드뷰의 panoId를 추출하여 로드뷰를 띄운다.
roadviewClient.getNearestPanoId(position, 50, function(panoId) {
    roadview.setPanoId(panoId, position); //panoId와 중심좌표를 통해 로드뷰 실행
});
</script>
	<script>
$(function(){
	var url = "https://api.odcloud.kr/api/15052413/v1/uddi:816ab9e3-dafe-4701-839b-c0c4d53adffe_201902261655?page=1&perPage=10&serviceKey=SLdtAgHWosK5tvRVL%2Bw87WA8CQz%2FvJIV8hmrV5Yocqqqwu21Ft5IhVLQX1aF6iY5k2pj3Xf%2BztwhMAedzEeF1A%3D%3D";
	$.getJSON(url, function(result) {  // getJSON 시작
	   
	        var data = result.data;
	        console.log(data);
	        var tb = $("<table />")
	        $(tb).append("<th>순번</th><th>약국명칭</th><th>약국소재지(도로명)</th><th>약국우편번호</th><th>약국전화번호</th>");
	        $.each(data, function(i, d) {
	          var row = $("<tr />").append(
	                        $("<td />").text(d["순번"]),
	                          $("<td />").text(d["약국명칭"]),
	                          $("<td class='address' />").text(d["약국소재지(도로명)"]),
	                          $("<td />").text(d["약국우편번호"]),
	                          $("<td />").text(d["약국전화번호"])                                 
	                      );
	          tb.append(row);
	        });
	        
	        $("body").append(tb);
	        
	        $(".address").on("click",function(){
	        	
	        	var pharmAddress = $(this).text();
	        	console.log(pharmAddress);
	        	
	            // 주소-좌표 변환 객체를 생성합니다
	            var geocoder = new kakao.maps.services.Geocoder();
	            
	            geocoder.addressSearch(pharmAddress, function(result, status) {
	      	      
	  	          // 정상적으로 검색이 완료됐으면 
	  	           if (status === kakao.maps.services.Status.OK) {
	  	      
	  	        	 var position = new kakao.maps.LatLng(result[0].y, result[0].x);
	  	              
	                  // 특정 위치의 좌표와 가까운 로드뷰의 panoId를 추출하여 로드뷰를 띄운다.
	                  roadviewClient.getNearestPanoId(position, 50, function(panoId) {
	                      roadview.setPanoId(panoId, position); //panoId와 중심좌표를 통해 로드뷰 실행
	                  });
	  	   
	  	          	} 
	  	     	 }); 
	        });
	});
});

</script>
</body>
</html>

 

드디어 맥북에 스프링을 깔았다! 

이클립스 오류로 심란했던 며칠 간의 기억으로 또 스프링을 깔았다가 뭔가 이것저것 문제가 생기면 어쩌나 걱정하며 

미루기도 했지만 언제까지 JSP만 공부할 수도 없고 그냥 스프링을 깔았다! 

너무 지나치게 겁을 먹은 건지 생각보다 쉽게 깔았다.

어차피 개발자 되면 환경 바뀔 때마다 해야하는 일일텐데 미리 공부한다 생각하며 오류에 좀 초연해져야하는 것 같다.. 

 

 

언제나 그렇듯 이번 설치는 블로그 선생님들의 은혜가 크다. 

일단 나는 배우는 사람 입장이기에 내가 따라하며 쉽고 유용했던 블로그들을 기록해두려한다.

그냥 무턱대고 남궁성 선생님 블로그 보고 스프링이 아니라 스프링부트를 깔았길래 일단 지워주고

다시 수업에 맞는 버전의 스프링을 받았다. 근데 설치 후에도 열리지 않아 보니까 jdk의 버전이 안맞아서 그런거였다..!

기존 dk 21 버전과 17버전이 깔려 있었는데 거기다 jdk11 버전을 추가해 깔고,

환경변수를 설정해준 후 다시 스프링을 다운 받아 실행했더니 잘 됐다!

jdk 11 버전으로 버전을 바꾸는 법은 아래 블로그를 참고했다. 

 

 

🌀 JDK 11 버전 변경 참고 블로그

 

https://llighter.github.io/install-java-on-mac/

 

맥에서 Brew로 자바 설치하기(feat. 자바버전 바꾸기)

 

llighter.github.io

 

잘 따라하기만 해도 알아서 된다. 

 

환경변수 설정까지 마치고 수업 때 쓰는 스프링 버전을 찾아 깔아주었다. 

 

 

내가 다운 받은 건 3.9.12

 

설치 후 들어가보면 '이 파일은 인터넷에서 어쩌고' 하는데 그냥 열기를 누르면 워크스페이스 설정이 나오고

워크스페이스까지 설정하면 맥북에서도 이런 화면을 만날 수 있다.

 

맥북으로 스프링 쓰는게 뭐 그렇게 대단한가 하는 사람도 있겠지만 

나의 경우는 국비 수업 내내 맥 쓴다고, 맥은 스프링 못깐다고(?) 계속 맥스라이팅을 당했다.. 그래서 겁이 많이 났는데

별거 아니었음. 앞으론 별거일지도.. 암튼 그래도 해보자고!

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

 

 

기본으로 이 코드가 설정이 되어야 하는데 난 지금까지 다른 설정으로 되어 기본 코드가 작성됐다.

그럴 땐 윈도우 Preferences에 들어가서 설정을 바꾸면 된다.

 

 

Workspace에서 바꿔주고, Web에서는 목록을 열어 html, css, jsp의 Encoding을 UTF-8로 바꿔준다.

 

 

그러면 이제 jsp 파일을 새로 만들 때마다 기본 설정이 UTF-8로 잘 된다!

 

AJAX 통신

 

 

값을 서버에서 전송하는 경우

 

ajaxClient3

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> 
<script>
$(function(){
	$("#buttonSubmit").on('click', function(){
		$.ajax({
			url : './ajaxServer3.jsp',
			type : 'post',
			dataType : 'json',
			success:function(data){
				$("#nickName").text(data.nickName);
				$("#ph_number").text(data.ph_number);
				$("#address").text(data.address);
			},
			error: function(err) {
				alert("실패 원인 : " + err);
			}
		});
	});
});
</script>
</head>
<body>
	<input id="buttonSubmit" type="button" value="제출">
	<p id="nickName"></p>
	<p id="ph_number"></p>
	<p id="address"></p>
</body>
</html>

 

ajaxServer3

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>

{ 
"nickName": "타바스코",
"ph_number" : "0101111111",
"address" : "연남동" 
}

{ “받아올 아이디값” : “넘겨줄 값” } 이 들어가면 된다.

 

 

 

값을 배열로 서버에서 전송하는 경우

 

ajaxClient4

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
	$(function() {
		$("#buttonSubmit").on('click', function() {
			$.ajax({
				url : './ajaxServer4.jsp',
				type : 'post',
				dataType : 'json',
				success : function(data) {
					$.each(data, function(i, d) {
						console.log(d.nickName);
						console.log(d.ph_number);
						console.log(d.adrress);

						$("#info").before("<p>" + data[i].nickName + "</p>");
						$("#info").before("<p>" + data[i].ph_number + "</p>");
						$("#info").before("<p>" + data[i].address + "</p>");
					});
				},
				error : function(err) {
					alert("실패 원인 : " + err);
				}
			});
		});
	});
</script>
</head>
<body>
	<input id="buttonSubmit" type="button" value="제출">
	<p id="info"></p>
</body>
</html>

each 문을 사용해 배열에서 값을 출력한다.

 

ajaxServer4

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
[
	{
		"nickName": "타바스코",
		"ph_number" : "01011111111",
		"address" : "서울"
	},
	{
		"nickName": "마요네즈",
		"ph_number" : "01033331111",
		"address" : "부산"
	}
]

 

 

 

 

 

ajax Gson 라이브러리 사용

데이터양이 많아졌을 때 효율적인 처리를 위한 라이브러리가 있다. → gson

데이터양이 적을 때는 직접 다 받아올 데이터를 써줄 수 있지만 양이 많으면 라이브러리로 데이터를 쉽게 받아올 수 있다.

 

 

서블릿을 이용해 Gson 사용해보기

 


 

앞으로 할 것 )

 

네이버의 API 를 이용해 검색창 구현

-블로그를 검색하는 기능

-서블릿 사용

 

네이버 블로그 검색 주소

https://openapi.naver.com/v1/search/blog?query=

이제 이걸 실행하는 화면을 만들 것이다.

 


 

질문) 

@AllArgsConstructor의 역할

@AllArgsConstructor 어노테이션은 클래스의 모든 필드를 파라미터로 받는 생성자를 자동으로 생성해주는 역할을 합니다. 이를 통해 개발자는 일일이 생성자를 작성할 필요 없이, 코드의 간결함과 유지보수성을 높일 수 있습니다.

출처) 노션 AI

 

 

+ Recent posts