인프런 커뮤니티 질문&답변

rubberramen님의 프로필 이미지
rubberramen

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

JPA와 DB 설정, 동작확인

MemberRepositoryTest 중 SQLGrammarException 에러 발생

해결된 질문

작성

·

952

0

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)

[질문 내용]
여기에 질문 내용을 남겨주세요.

 


안녕하세요?
스프링 부트 & JPA 강의 듣고 있는 수강생입니다.

 

MemberRepository 테스트 중 SQLGrammarException이 발생하여 테스트가 실패합니다.

MemberRepository의 save 메서드 아래 em.persist(member) 부분 때문에 발생한 것으로 보이는데 그 이유를 잘 모르겠습니다.

영한님의 야생형 루트에 따라 JPA 수업을 아직 안듣고, 바로 실전으로 들어와서 그런제 jpa에 대한 기초가 약합니다.

로그 남깁니다. 도움 부탁드리겠습니다.

 


2023-02-06 11:19:45.353 INFO 10204 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@6d167f58 testClass = MemberRepositoryTest, testInstance = jpabook.jpashop.MemberRepositoryTest@7682bf66, testMethod = testMember@MemberRepositoryTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@3403e2ac testClass = MemberRepositoryTest, locations = '{}', classes = '{class jpabook.jpashop.JpashopApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@38102d01, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@610f7aa, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@2118cddf, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@76ed1b7c, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@1d8bd0de, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@293a5bf6], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@78b8f818]; rollback [false]

2023-02-06 11:19:45.625 DEBUG 10204 --- [ main] org.hibernate.SQL : call next value for hibernate_sequence

2023-02-06 11:19:45.627 WARN 10204 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 90036, SQLState: 90036

2023-02-06 11:19:45.628 ERROR 10204 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Sequence "HIBERNATE_SEQUENCE" not found; SQL statement:

call next value for hibernate_sequence [90036-200]

2023-02-06 11:19:45.666 WARN 10204 --- [ main] o.s.test.context.TestContextManager : Caught exception while invoking 'afterTestMethod' callback on TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener@20435c40] for test method [public void jpabook.jpashop.MemberRepositoryTest.testMember()] and test instance [jpabook.jpashop.MemberRepositoryTest@7682bf66]

org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only

at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:752) ~[spring-tx-5.3.2.jar:5.3.2]

at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) ~[spring-tx-5.3.2.jar:5.3.2]

at org.springframework.test.context.transaction.TransactionContext.endTransaction(TransactionContext.java:131) ~[spring-test-5.3.2.jar:5.3.2]

at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:255) ~[spring-test-5.3.2.jar:5.3.2]

at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:441) ~[spring-test-5.3.2.jar:5.3.2]

at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:94) ~[spring-test-5.3.2.jar:5.3.2]

at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) ~[spring-test-5.3.2.jar:5.3.2]

at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) ~[junit-4.13.1.jar:4.13.1]

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) ~[spring-test-5.3.2.jar:5.3.2]

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) ~[spring-test-5.3.2.jar:5.3.2]

at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) ~[junit-4.13.1.jar:4.13.1]

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) ~[junit-4.13.1.jar:4.13.1]

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) ~[junit-4.13.1.jar:4.13.1]

at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) ~[junit-4.13.1.jar:4.13.1]

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) ~[junit-4.13.1.jar:4.13.1]

at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) ~[spring-test-5.3.2.jar:5.3.2]

at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) ~[spring-test-5.3.2.jar:5.3.2]

at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) ~[junit-4.13.1.jar:4.13.1]

at org.junit.runners.ParentRunner.run(ParentRunner.java:413) ~[junit-4.13.1.jar:4.13.1]

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) ~[spring-test-5.3.2.jar:5.3.2]

at org.junit.runner.JUnitCore.run(JUnitCore.java:137) ~[junit-4.13.1.jar:4.13.1]

at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) ~[junit-rt.jar:na]

at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) ~[junit-rt.jar:na]

at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) ~[idea_rt.jar:na]

at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) ~[junit-rt.jar:na]

at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235) ~[junit-rt.jar:na]

at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54) ~[junit-rt.jar:na]

org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [call next value for hibernate_sequence]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement

at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:259)

at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)

at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)

at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)

at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)

at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)

at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)

at jpabook.jpashop.MemberRepository$$EnhancerBySpringCGLIB$$2b302b5c.save(<generated>)

at jpabook.jpashop.MemberRepositoryTest.testMember(MemberRepositoryTest.java:26)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.base/java.lang.reflect.Method.invoke(Method.java:566)

at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)

at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)

at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)

at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)

at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)

at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)

at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)

at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)

at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)

at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)

at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)

at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)

at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)

at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)

at org.junit.runners.ParentRunner.run(ParentRunner.java:413)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)

at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)

at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)

at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)

at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)

at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)

at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

Caused by: org.hibernate.exception.SQLGrammarException: could not prepare statement

at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:63)

at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)

at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)

at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:186)

at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareStatement(StatementPreparerImpl.java:75)

at org.hibernate.id.enhanced.SequenceStructure$1.getNextValue(SequenceStructure.java:93)

at org.hibernate.id.enhanced.NoopOptimizer.generate(NoopOptimizer.java:40)

at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:523)

at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:115)

at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185)

at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128)

at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55)

at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102)

at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:720)

at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:706)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.base/java.lang.reflect.Method.invoke(Method.java:566)

at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)

at com.sun.proxy.$Proxy86.persist(Unknown Source)

at jpabook.jpashop.MemberRepository.save(MemberRepository.java:16)

at jpabook.jpashop.MemberRepository$$FastClassBySpringCGLIB$$a3e1a60b.invoke(<generated>)

at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)

at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)

... 38 more

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Sequence "HIBERNATE_SEQUENCE" not found; SQL statement:

call next value for hibernate_sequence [90036-200]

at org.h2.message.DbException.getJdbcSQLException(DbException.java:576)

at org.h2.message.DbException.getJdbcSQLException(DbException.java:429)

at org.h2.message.DbException.get(DbException.java:205)

at org.h2.message.DbException.get(DbException.java:181)

at org.h2.command.Parser.readSequence(Parser.java:7678)

at org.h2.command.Parser.readTermWithIdentifier(Parser.java:4593)

at org.h2.command.Parser.readTerm(Parser.java:4311)

at org.h2.command.Parser.readFactor(Parser.java:3343)

at org.h2.command.Parser.readSum(Parser.java:3330)

at org.h2.command.Parser.readConcat(Parser.java:3305)

at org.h2.command.Parser.readCondition(Parser.java:3108)

at org.h2.command.Parser.readExpression(Parser.java:3059)

at org.h2.command.Parser.parseCall(Parser.java:6480)

at org.h2.command.Parser.parsePrepared(Parser.java:905)

at org.h2.command.Parser.parse(Parser.java:843)

at org.h2.command.Parser.parse(Parser.java:815)

at org.h2.command.Parser.prepareCommand(Parser.java:738)

at org.h2.engine.Session.prepareLocal(Session.java:657)

at org.h2.server.TcpServerThread.process(TcpServerThread.java:278)

at org.h2.server.TcpServerThread.run(TcpServerThread.java:183)

at java.base/java.lang.Thread.run(Thread.java:834)

at org.h2.message.DbException.getJdbcSQLException(DbException.java:576)

at org.h2.engine.SessionRemote.done(SessionRemote.java:611)

at org.h2.command.CommandRemote.prepare(CommandRemote.java:85)

at org.h2.command.CommandRemote.<init>(CommandRemote.java:51)

at org.h2.engine.SessionRemote.prepareCommand(SessionRemote.java:481)

at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1235)

at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:76)

at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:352)

at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:337)

at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java)

at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$1.doPrepare(StatementPreparerImpl.java:90)

at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:176)

... 62 more

org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only

at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:752)

at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)

at org.springframework.test.context.transaction.TransactionContext.endTransaction(TransactionContext.java:131)

at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:255)

at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:441)

at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:94)

at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)

at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)

at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)

at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)

at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)

at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)

at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)

at org.junit.runners.ParentRunner.run(ParentRunner.java:413)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)

at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)

at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)

at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)

at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)

at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)

at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

 

답변 2

2

안녕하세요. 고무라면님, 공식 서포터즈 OMG입니다.

아래 링크의 영한님 답변을 참고해주세요 :)

https://www.inflearn.com/questions/610904/memberrepositorytest-%EC%97%90%EB%9F%AC%EC%9E%85%EB%8B%88%EB%8B%A4



감사합니다.

rubberramen님의 프로필 이미지
rubberramen
질문자

application.yml 파일을 꼼꼼하게 확인했다고 생각했는데, 거기에서 잘못이 있었네요.

도움 감사드립니다!

0

2023-02-06 11:19:45.628 ERROR 10204 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Sequence "HIBERNATE_SEQUENCE" not found; SQL statement:

The line above is the root cause of the problem. It seems the sequence is not created, so code errors out while expecting it to exist.

rubberramen님의 프로필 이미지
rubberramen

작성한 질문수

질문하기